PDA

View Full Version : Doing a thing only once another thing is complete



dog
12-01-2009, 07:24 PM
Hello everybody,

I hope it is okay to ask for help with Mootools here.

I am using Fx.Slide to get the main content of my page to slide in when the DOM is ready. Then when someone clicks on a link to go to a different page I want the main content to slide out before the browser changes location.

Here is the HTML for example:
<ul id="menu">
<li><a href="page2.html">Page 2</a></li>
</ul>

<div id="mainContent">content here!</div>

I have been able to get 'mainContent' to slide in when the DOM is ready and I can get the the content to slide out when 'menu' is clicked OR to get the window to change location when 'menu' is clicked but I can't get both things to happen. Does anyone know how to I edit my javascript so that the window changes location only when the slide out is complete?

Here's an example of the javascript I've written:

window.addEvent('domready', function(){

var myPanal = new Fx.Slide('mainContent').hide();

myPanal.slideIn();

$('menu').addEvent('click', function(e){
myPanal.slideOut();

myPanal.addEvent('complete', function() {
window.location="../page2.html/";
});

});

});

Thanks for any help!

dog
12-01-2009, 08:00 PM
Ok... it looks like I've solved the initial problem:

Here's the code that's working for me now:


window.addEvent('domready', function(){

var myPanel = new Fx.Slide('mainContent').hide();

myPanel.slideIn();

var myPanel2 = new Fx.Slide('mainContent').hide();

$('menu').addEvent('click', function(e){
e.stop();
myPanel2.toggle();
});

myPanel2.addEvent('complete', function() {
window.location= "page2.html";
});
});

I would still appreciate feedback on this because I know my javascript skill leaves much room for improvement.

Also, using this method I am going to have to write a bit of script for each different menu item. Can anyone suggest how I might write one bit of script that would cover all the different links in a menu?

Thanks again!

jscheuer1
12-02-2009, 05:37 AM
<script type="text/javascript">
window.addEvent('domready', function(){
var myPanel = new Fx.Slide('mainContent').hide(), a;
myPanel.slideIn();
$$('.menu').addEvent('click', function(e){
e.stop();
a = e.target.href? e.target.href : e.target.getElement('a').href;
myPanel.addEvent('complete', function(){
location = a;
});
myPanel.slideOut();
});
});
</script>
</head>
<body>
<ul>
<li class="menu"><a href="page2.html">Page 2</a></li>
<li class="menu"><a href="page3.html">Page 3</a></li>
</ul>

<div id="mainContent">content here!</div>
</body>

dog
12-02-2009, 10:29 AM
John,

That's amazing! Thanks very much.

Do you think you could explain the code to me? I have question after question but it might be easier if you were to explain it line by line.

I'll ask my questions anyway so that you get an idea of where my mind is at:

Line 3: what is 'a' and how come you can just add it on the end like that? how would I have known to do that?

Line 5: what is 'e'? why does it stop in line 6 and what does that mean?

Line 7: I can see that you are setting 'a' to be the target href of the links that get clicked but I don't understand why it seems that you are doing this three times or what the ':' does.

Thanks again!

Laurence

jscheuer1
12-02-2009, 10:50 AM
Line 3: We are already in a limited scope due to being inside the domready function, so it is OK to declare a variable (a) for later use. Once the var keyword is invoked you may (and it is in fact preferable to) declare all variables desirable for that scope, comma separated.

Line 5: You had that in your code. The e is the event object. Using e.stop(), which you also had, prevents the event (link) from firing normally.

Line 7: When you click on the LI, presumably you want the link's href to fire after the slideOut(). If you are also clicking on the link, it is the event's target, but if you were to click on the LI outside the link, the link would not be the target. So we just find the link. The:


a = condition? true_value : false_value;

construct is shorthand, equivalent to:


if(condition) {
a = true_value;
}
else {
a = false_value;
}

dog
12-04-2009, 11:59 AM
Thanks very much John,

It took me little while but I think I understand.

Best wishes and good luck with the good work,
Laurence

jscheuer1
12-04-2009, 05:03 PM
If there is anything else you want to know, feel free to ask. Scope, for instance, and how it relates here and to scripts in general can be an elusive concept at first. It is however one of the most important concepts in almost all coding languages.

dog
12-04-2009, 06:05 PM
Sure. I am looking to learn.

So tell me about scope.

One other question is why you apply the class to the LI and not to the A? In my real page I apply class='selected' to the LI relating to the selected menu item. For this reason I have class='menu' applied directly to the A. I haven't seen any problem in doing this but there may be a reason that you wrote...


<ul>
<li class="menu"><a href="page2.html">Page 2</a></li>
<li class="menu"><a href="page3.html">Page 3</a></li>
</ul>

...and not...


<ul>
<li><a class="menu" href="page2.html">Page 2</a></li>
<li><a class="menu" href="page3.html">Page 3</a></li>
</ul>

jscheuer1
12-05-2009, 04:36 AM
Scope is an interesting concept. It's a little like keeping a secret. Say you have two friends. One of them knows some secret and the other doesn't. There is always a chance that the one who knows might blab. But if they do, it will depend upon how they do it as to what might happen. Let's see two of the many ways that can work out in javascript -

Example One:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript">
var a = 'I don\'t know.';
(function(){
var a = 'I know something.';
onload = function(){
alert(a);
};
})();
</script>
</head>
<body>
<div>
<input type="button" onclick="alert(a);" value="What's a?">
</div>
</body>
</html>

In the above example, when the page loads it will alert:

I know something.

But when you click the button, even though the page has loaded:

I don't know.

If we make one subtle change -

Example Two:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript">
var a = 'I don\'t know.';
(function(){
a = 'I know something.';
onload = function(){
alert(a);
};
})();
</script>
</head>
<body>
<div>
<input type="button" onclick="alert(a);" value="What's a?">
</div>
</body>
</html>

Then both the onload event and the button click will alert:

I know something.

Now, one of the primary reasons why there can be javascript conflicts is that two scripts use the same name (in this case a) to refer to two different things. Wouldn't it be great if, whenever possible, all scripts using a would keep its value a secret from other scripts? By limiting the scope as in Example One, that's just what happens.

In my code from post #3 in this thread, a would be a secret from any code not executed inside the domready event. At the same time it is available to the .menu onclick event inside the domready event to do with as it will.

Hopefully this clears things up a bit about that.

As to the class on the LI - I wanted clicking on the LI or on the link in the LI to trigger the event because, not knowing the exact markup and style that would be in effect for this, it would always be possible that the user might click on the LI, but be missing the link. They would probably be expecting to be taken to the page that the link's text describes, but without keying off of the LI, the scrpt code couldn't do that, and in fact nothing would happen.

dog
12-07-2009, 03:53 PM
Once again, thanks very much. Great explanation! Now I understand the basics of scope.

I have one more problem with which I need help.

In applying this code to a particular menu I am encountering new problems. In this menu there are IMGs and SPANs inside A tags. When one clicks on any part of the link that is a picture or within the SPAN nothing happens and Error Console say, 'e.target.getElement("a") is null'.

Here is the markup for the menu:

<script type="text/javascript">
window.addEvent('load', function(){
var myPanel = new Fx.Slide('mainContent').hide(), a;
myPanel.slideIn();
$$('.menu').addEvent('click', function(e){
e.stop();
a = e.target.href? e.target.href : e.target.getElement('a').href;
myPanel.addEvent('complete', function(){
location = a;
});
myPanel.slideOut();
});
});
</script>
</head>
<body>
<div class="menu">
<div class="tab">
<div class="image"><a href="page2.html"><img src="image2.jpg" /></a></div>
<div class="label"><a href="page2.html">Page 2 <span>(Intermediate)</span></a></div>
</div>
<div class="tab">
<div class="image"><a href="page3.html"><img src="image3.jpg" /></a></div>
<div class="label"><a href="page3.html">Page 3 <span>(Advanced)</span></a></div>
</div>
</div>

<div id="mainContent">content here!</div>
</body>

I can see a solution using CSS that would allow me to do away with the IMG and the SPAN tags but I would like to discuss a javascript solution because it is something I want to learn about.

I've been looking into Mootools, utilities/selectors (http://mootools.net/docs/core/Utilities/Selectors#Element) and can only find ways of selecting things that are contained within other things but how might one select the container? Can I click on the span and get the href of the anchor that contains the span?

Thanks,

Laurence

jscheuer1
12-07-2009, 06:56 PM
This should be easy:


a = e.target.href || e.target.getElement('a') || e.target.getParents('a') || null;

But Mootools is poorly coded for IE in that if there are no child nodes, getElement throws an error instead of returning a falsy value. And getParents just doesn't seem to work at all for IE, at least not like that. Both are fine in all other browsers I tested though. So we can do some IE gynastics (highlighted and green in the below) to get it to work out in IE and leave others unaffected. The only pitfall in IE is that if there will be nested elements within the element(s) nested within the a tag, more code will be needed. The getParents method in Mootools takes care of that for all others.

Here's the finished code with some other stuff (highlighted and black) added so that on a real page where the domready may take a while to fire, the mainContent will remain unseen until it slides in:


<script type="text/javascript">
document.write('<style type="text/css">#mainContent {visibility: hidden;}<\/style>');
window.addEvent('domready', function(){
var MC = $('mainContent'), myPanel = new Fx.Slide(MC).hide(), a;
myPanel.addEvent('start', function(){MC.setStyle('visibility', 'visible');});
myPanel.slideIn();
$$('.menu').addEvent('click', function(e){
e.stop();
/*@cc_on @*/
/*@if(@_jscript_version >= 5)
try{
@end @*/
a = e.target.href || e.target.getElement('a') || e.target.getParents('a') || null;
/*@if(@_jscript_version >= 5)
}catch(err){a = e.target.parentNode && e.target.parentNode.href? e.target.parentNode : null;}
@end @*/
if(a){
myPanel.addEvent('complete', function() {
location = a;
});
myPanel.slideOut();
}
});

});
</script>

Also note highlighted and red - a test for all browsers to make absolutely sure we found a link before attempting to proceed with the slide out and page change.

dog
12-08-2009, 11:39 AM
Hey John,

Thanks once again. I am really impressed. I am surprised that, with the exception of the green highlighted stuff, I understand almost all of this.

The bit that writes CSS to hide #mainContent until Mootools kicks-in is simple genius. I love it! Hopefully that will do away with the flicker that I was getting on some browsers when going from one page to another.

Could you please tell me a little more about 'e.target.href'. You wrote that 'e' is the event object. In this case it is the thing that gets clicked, right? Until you click something inside '.menu' it could be anything, true? And, 'target' and 'href', how do they differ? In 'e.target.getElement('a')' there is no mention of href so why is it needed in 'e.target.href'?

With more thanks, and best wishes,
Laurence

jscheuer1
12-08-2009, 01:46 PM
You are right about e and e.target. The href thing too, except there is an oddity about how that works out in javascript that makes the code much shorter than it would otherwise have to be.

I know what you might be thinking - maybe it's this shortcut that doesn't work in IE. Nope, I tested it written out the long way, no help.

Anyways, the trick is that an anchor link tag, unlike all other tags, when viewed by javascript as an object (how it sees all other tags that are part of the DOM), is seen as a string. That string is the anchor link's href. So the first test:


e.target.href

determines whether or not we have a link, as well as returning the href because if it has no href, it's not an anchor link. The second test:


e.target.getElement('a')

since it can only (when working as expected, and as it does in all non-IE I tested), returns either an a tag or a falsy value, when it returns a truthy value, it will already be the href value.

Similarly for the third test.

As you are probably also interested, this stuff:


/*@cc_on @*/
/*@if(@_jscript_version >= 5)


@end @*/

when placed around a code block makes that block appear to be a commnet to all but IE 5+.

The code I'm hiding is an ordinary try/catch which catches the errors thrown by IE in some of the situations it may encounter with that line:


a = e.target.href || e.target.getElement('a') || e.target.getParents('a') || null;

and performs the required actions IE otherwise cannot perform when an error is thrown by it for that line.

jscheuer1
12-09-2009, 04:39 AM
Ooops! I discovered another problem with IE. If the element that was clicked on (e.target) is an image tag with a src attribute, IE sees that attribute as qualifying as an href property of the image tag. So this messes things up. I've decided to therefore do everything in a manner that IE can handle and that also will work in other browsers:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://mootools.net/download/get/mootools-1.2.4-core-yc.js"></script>
<script type="text/javascript" src="mootools-1.2.4.2-more.js"></script>
<script type="text/javascript">
document.write('<style type="text/css">#mainContent {visibility: hidden;}<\/style>');
window.addEvent('domready', function(){
var MC = $('mainContent'), myPanel = new Fx.Slide(MC).hide(), lnks = $(document.body).getElements('a'), a, t, tp, tc, cn;
myPanel.addEvent('start', function(){MC.setStyle('visibility', 'visible');});
myPanel.slideIn();
$$('.menu').addEvent('click', function(e){
e.stop();
cn = 0;
t = tp = tc = e.target;
while(lnks.indexOf(tp) < 0 && lnks.indexOf(tc) < 0){
tp = tp.parentNode || {parentNode: null};
tc = t.getElementsByTagName('a')[cn++] || null;
}
a = lnks.indexOf(tp) > -1? tp : lnks.indexOf(tc) > -1? tc : null;
if(a && a.href){
myPanel.addEvent('complete', function() {
location = a;
});
myPanel.slideOut();
}
});

});
</script>
</head>
<body>
<ul>
<li class="menu"><a href="page2.html">Page 2</a></li>
<li class="menu"><a href="page3.html"><span>Page 3</span></a></li>
<li class="menu"><a name="ulMenu">Dummy</a></li>
</ul>

<div id="mainContent">content here!</div>
</body>
</html>