SitePoint Sponsor

User Tag List

Results 1 to 7 of 7
  1. #1
    SitePoint Addict Smola's Avatar
    Join Date
    Mar 2005
    Posts
    260
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Question Animation Problems

    I am working doing some rudimentary animation with javascript simply animating some div elements. I am sure that there is a much more efficient way of doing things, but this is what I've got:

    Code javascript:
    var changeHeight = {
     
    	init: function() {
     
    		var elements = Core.getElementsByClass("tab");
    		changeHeight.steps = 10;		//number of animation steps
    		changeHeight.maxSize = 150;		//max enlargement size in pixels
    		changeHeight.maxed = false;		//will be used for element enlargement
     
     
    		for (var i = 0; i < elements.length; i++) {
    						Core.addEventListener(elements[i],"mouseover",changeHeight.enlarge);
    			Core.addEventListener(elements[i],"mouseout",changeHeight.shrink);
     
    		}
    	},
     
     
    	enlarge: function() 
    	{
     
    		if (!changeHeight.curHeight)     //checking for first function evaluation
    		{
    			changeHeight.curHeight = parseInt(Core.getComputedStyle(this,"height"),10);
    			changeHeight.startHeight = changeHeight.curHeight;
    			changeHeight.elem = this;
    		}
     
    		if (changeHeight.curHeight < changeHeight.maxSize) 
    		{
    			changeHeight.step = Math.round((changeHeight.maxSize-changeHeight.curHeight)/changeHeight.steps);
    			if (changeHeight.step < 1) return;
    			changeHeight.curHeight += changeHeight.step;
    			changeHeight.elem.style.height = changeHeight.curHeight + "px";
    			setTimeout(changeHeight.enlarge,.5);
    		}
    	},
     
     
     
    	shrink: function() {
    		delete changeHeight.curHeight;
    		this.style.height = changeHeight.startHeight + "px";
    	}
     
     
    }

    and the html in question:

    HTML Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    <title>Java Testing</title>
    
    <script type="text/javascript" src="scripts/core.js"></script>
    <script type="text/javascript" src="scripts/element_animation.js"></script>
    
    
    <style type="text/css">
    
    .tab {
    	width:100px;
    	min-height:10px;
    	border:1px solid black;
    	background-color:#00CC00;
    	margin: 10px;
    	}
    	
    .button {
    	height:30px;
    	text-decoration:none;
    	color:#000000;
    	width:100%;
    	text-align:center;
    	}
    	
    #compare {
    	height:200px;
    	width:100px;
    	background-color:#00CC00;
    	border: 1px solid black;
    	position:absolute;
    	left: 150px;
    	top:10px;
    	}
    	
    </style>
    
    </head>
    
    <body>
    
    <div id="poop">
    
    	<div class="tab">
    		<a href="#" class="button">Click Me</a>
    	</div>
    	<div class="tab">
    		<a href="#" class="button">Click Me</a>
    	</div>
    	<div id="compare">
    	
    	</div>
    
    </div>
    
    </body>
    </html>
    Hovering over each div element works just like it's supposed to. However, when i go from hovering over the div to hovering over the link, the animation resets and starts again. Basically it expands the element twice (not twice as long, just original to expanded and then original to expanded again). This also happens when i move the mouse off the link, I imagine because some small part of the margin or padding makes it hover over the div again. Anyway, why is it activating twice and how do I stop it??
    Humbly,

    Smola

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,696
    Mentioned
    101 Post(s)
    Tagged
    4 Thread(s)
    The solution is to stop it closing when it's not appropriate.
    See http://www.quirksmode.org/js/events_mouse.html for details.

    Code javascript:
    shrink: function(evt) {
        evt = evt || window.event;
        var targ = evt.relatedTarget || evt.fromElement;
        if (targ && targ.id !== 'poop') {
            return;
        }
        delete changeHeight.curHeight;
        this.style.height = changeHeight.startHeight + "px";
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Addict Smola's Avatar
    Join Date
    Mar 2005
    Posts
    260
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Event bubbling!! AHHH! Of course it had to be complicated...anyway thanks for your response and the link. It was very enlightening. However, it isn't quite working. Once it opens, the dang thing never shrinks! The error console says evt has no properties. So I tried seeing if I could just do a straight declaration to the window event by removing the evt from the function's argument list and changing:

    Code javascript:
    evt = evt || window.event;
    //changed to
    var evt = window.event;

    but I get the same error. I was also curious why evt has to be passed as an argument, especially when I have had issues passing arguments to event handlers in the past. Why can't I just define it inside of the function?

    Any ideas on how to fix the error?

    EDIT: It DOES work, with one peculiar exception: It will not shrink if the mouse exits the bottom of the div, only on the sides. It's very strange. That website also helped me understand that evt is the event that is automatically passed to the function via the event listener. Once again thanks for the help, and can you explain why exiting through the bottom of the div prevents it from shrinking?
    Last edited by Smola; Jun 4, 2008 at 13:11. Reason: Update
    Humbly,

    Smola

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,696
    Mentioned
    101 Post(s)
    Tagged
    4 Thread(s)
    Let's take a closer look at things then.

    There are two event properties that we can make good use of, target and relatedTarget (IE uses srcElement and fromElement)

    With the onmouseout event, there are several different elements involved depending on where the mouse is moving from and to.

    • From div to outside
      targ = <div id="div2" class="tab" style="height: 146px;">
      relTarg = <div id="poop"> or <html xmlns="http://www.w3.org/1999/xhtml">
    • From div to anchor
      targ = <div id="div2" class="tab" style="height: 146px;">
      relTarg = <a class="button" href="#">
    • From anchor to div
      targ = <a class="button" href="#">
      relTarg = <div id="div2" class="tab" style="height: 146px;">


    It's only the first case that we want he event to fire on, so we have to rule out the other two.

    The last case is easy to rule out, if the relTarg has a class of "tab" then we return out of the function.

    The second case is the tricky one. It can be done explicitly so that we just check if the relTarg isn't an anchor, but that's no good should the html code change. What we want is a way of starting from the target, and checking if the relTarg is anywhere inside that target. If it is then we don't want to fire the event.

    Happily there is a walkTheDOM function that we can use from Douglas Crockford.

    Code javascript:
    var walkTheDOM = function walk(node, func) {
        func(node);
        node = node.firstChild;
        while (node) {
            walk(node, func);
            node = node.nextSibling;
        }
    };

    All we need is a variable to keep track on if we find the relTarg inside, and we can then do some checking.

    Code javascript:
    var isInside = false;
    ...
    walkTheDOM(targ, function(node) {
    	if (node === relTarg) {
    		isInside = true;
    	}
    });

    Here is that shrink method in full

    Code javascript:
    shrink: function(evt) {
    	evt = evt || window.event;
    	var targ = evt.target || evt.srcElement;
    	var relTarg = evt.relatedTarget || evt.fromElement;
    	var isInside = false;
    	if (relTarg) {
    		walkTheDOM(targ, function(node) {
    			if (node === relTarg) {
    				isInside = true;
    			}
    		});
    		if (isInside || relTarg.className === 'tab') {
    			return;
    		}
    	}
    	delete changeHeight.curHeight;
    	this.style.height = changeHeight.startHeight + "px";
    }

    That now seems to test with no trouble.

    edit: I'll explore another method where we just confirm that the first case is what we're after.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,696
    Mentioned
    101 Post(s)
    Tagged
    4 Thread(s)
    Here is a much better way, where we check if the mouse target is outside of the div with the class called "tab"

    Code javascript:
    	shrink: function(evt) {
    		evt = evt || window.event;
    		var targ = evt.target || evt.srcElement;
    		var relTarg = evt.relatedTarget || evt.fromElement;
    		var outerTarget = false;
    		if (targ && targ.className === 'tab') {
    			while (targ.parentNode) {
    				targ = targ.parentNode;
    				if (targ === relTarg) {
    					outerTarget = true;
    				}
    			}
    			if (outerTarget) {
    				delete changeHeight.curHeight;
    				this.style.height = changeHeight.startHeight + "px";
    			}
    		}
    	}   
    }

    or perhaps even

    Code javascript:
    	shrink: function(evt) {
    		evt = evt || window.event;
    		var targ = evt.target || evt.srcElement;
    		var relTarg = evt.relatedTarget || evt.fromElement;
    		var outerTarget = false;
    		if (targ && targ.className === 'tab') {
    			if (function (targ, relTarg) {
    				while (targ.parentNode) {
    					targ = targ.parentNode;
    					if (targ === relTarg) {
    						return true;
    					}
    				}
    				return false;
    			}(targ, relTarg)) {
    				delete changeHeight.curHeight;
    				this.style.height = changeHeight.startHeight + "px";
    			}
    		}
    	}   
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  6. #6
    SitePoint Addict Smola's Avatar
    Join Date
    Mar 2005
    Posts
    260
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    pmw, I truly thank you for the time you've spent helping me (It's working like a charm). I look forward to getting more assistance from you as I continue to learn Javascript! Cheers!
    Humbly,

    Smola

  7. #7
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,696
    Mentioned
    101 Post(s)
    Tagged
    4 Thread(s)
    You're very welcome Smola.

    I just realised on looking back over that last example that the outerTarget line is no longer required. The function parameters that are being passed in order to prevent the targ variable from changing outside of it, don't need to be passed at all when a slightly different technique is used.

    Code javascript:
    shrink: function(evt) {
            evt = evt || window.event;
            var targ = evt.target || evt.srcElement;
            var relTarg = evt.relatedTarget || evt.fromElement;
            if (targ && targ.className === 'tab') {
                if (function () {
                    var el = targ;
                    while (el.parentNode) {
                        el = el.parentNode;
                        if (el === relTarg) {
                            return true;
                        }
                    }
                    return false;
                }()) {
                    delete changeHeight.curHeight;
                    this.style.height = changeHeight.startHeight + "px";
                }
            }
        }   
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript


Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •