Forum Moderators: open

Message Too Old, No Replies

addEventListener --- first time user

         

vol7ron

10:04 pm on Mar 7, 2008 (gmt 0)

10+ Year Member



Like I said, first time user of the attachEventListener. I'm trying to dynamically create a multi-state rollover menu. One image mouseover, many images mouseoff, using CSS sprites.

Instead of looping through the elements, i was manually setting a variable to a new link class (commented out in createLinks). Then I was manually setting onmouseover="link1.highlight()", which was working, but now I'm trying to late-bind it. So hopefully I can further adjust this code to make it fully automated in future.

Any help is appreciated to get attachEventListener to work. Note: at the moment pageLoad() only calls createLinks() and getObjById() can be replaced with getElementById()

JavaScript

   /* MENU FUNCTIONS
---------------- */
var lnk = new Array();
var obj = new Array();
function createLinks() {
//link1 = new linkClass('btn_home',null,0,-288,-72, 72, 33); link1.formatLink();

var d = document.getElementsByTagName('div');
for(i=0; i < d.length; i++) {
var e = d[i].getElementsByTagName('a');
if (d[i].className.match(/.+change/g)) {
for(j=0; j < e.length; j++) {
lnk[lnk.length] = e[j]; // adds a node to the array
last = lnk.length-1;
l = lnk[last];
l.id = "button_" + (last<10?"0"+last:last);
obj[last] = new linkClass(l.id,null,0,-288,-72,72,33); obj[last].formatLink();
//d[i].addEventListener("onmouseover",function(){eval("obj["+last+"].highlight()")} , false);
//e[j].addEventListener("onmouseout", function(){eval("obj["+last+"].changeImage()")}, false);

obj[last].highlight();
obj[last].changeImage();
}
}
}
}

function linkClass(objId, obj, startPos, maxPos, step, width, height) {
this.imgWidth = width;
this.imgHeight = height;
this.obj = obj!=null?obj:getObjById(objId);
this.objId = objId;
this.posCurr = startPos;
this.posMax = maxPos;
this.posStart = startPos;
this.posStep = step;
this.tmpDelay = 0;
this.timeoutId = 0;
}
linkClass.prototype.delay = 125;
linkClass.prototype.changeImage = changeImage;
linkClass.prototype.formatLink = formatLink;
linkClass.prototype.highlight = highlight;

function formatLink () {
with (this.obj.style) {
var w = this.imgWidth;
var h = this.imgHeight;
width = w < 0 ? w * -1 : w + "px";
height = h < 0 ? h * -1 : h + "px";
}
}

function highlight(href) {
with (this) {
window.clearTimeout(timeoutId); // clear last timeout
posCurr = posStart;
obj.style.background = "transparent url(Menu_Home.png) no-repeat " + posCurr + "px 0px;";
obj.href = href;
}
}

function changeImage(paramDelay) {
with (this) {
tmpDelay = (typeof paramDelay == 'undefined')?delay:paramDelay;
if (posCurr > posMax) {
obj.style.background = "transparent url(Menu_Home.png) no-repeat " + posCurr + "px 0px;";
posCurr += posStep;
timeoutId = window.setTimeout(function (){changeImage(tmpDelay);},tmpDelay); // passing a function solves errors
}
}
}

HTML
<body onload="javascript:pageLoad();">
<div id="body">
<div id="header">
<div class="menu">
<div class="img">&nbsp;&nbsp;&nbsp;</div>
<div class="img btn change" ><a href="#"></a></div>
<div class="img btn change" ><a href="#"></a></div>
<div class="img">&nbsp;&nbsp;&nbsp;</div>
</div>
</div>
</div>
</body>


CSS

body {
background: #EEEEFF;
font-family: verdana, arial, "lucida console", sans-serif;
font-size: 12px;
margin-left: auto;
margin-right: auto;
margin-top: 0;
text-align: center;
width: 800px;
}
div#body {
border: 1px solid #336666;
background: url(bg.png) repeat top left;
width: 800px;
}
div#header {
background-image: url(Work_01.png);
height: 175px;
position: relative;
text-align: left;
width: 800px;
}
div#header .menu {
bottom: 0px;
display: inline;
float: left;
margin-bottom: 25px;
position: absolute;
text-align:center;
}
/* BTN DEV TESTING FOR MENU */
div.btn a {
display: inline;
float: left;
height: 33px;
background: transparent url(Menu_Home.png) no-repeat -216px 0px;
}
div.divider {
background-color: #666699;
height: 1px;
margin: 3px auto 5px 0px;
padding: 0px 0px 0px 0px;
width: 100%;
}
div.img {
display:inline;
float:left;
}
div.img img {
border: 0px solid black;
}

[edited by: vol7ron at 10:15 pm (utc) on Mar. 7, 2008]

vol7ron

10:11 pm on Mar 7, 2008 (gmt 0)

10+ Year Member



One more thing: Menu_Home.png is 288x33px. It is 4 images in one, each image is 72px wide and 33px high.

This is in case you want to test it out. Also, for the time being both links will be the same. It'll be the same image side by side, the difference is the animation only goes off on mouseoff.

gergoe

3:40 am on Mar 8, 2008 (gmt 0)

10+ Year Member



Event names in addEventListener should appear without the "on" prefix, so it should appear as:
d.addEventListener("mouseover",function(){this.highlight();} , false);

The additional problem is the value of [i]last, which will not have the expected value at the time of executing the onmouseover event handler, it will always be set to the last one, regardless of the source of the event handler. Using this in this context will resolve to the source of the event handler, so either the <div>, either the <a>.

If you would like to keep track of the the linkClass instance similar way as you have done, then move the even handler definitions into a new function:

function attachEventHandlers(oDiv, oLink, linkObj) {
oDiv.addEventListener("mouseover",function(){linkObj.highlight();} , false);
oLink.addEventListener("mouseout", function(){linkObj.changeImage();}, false);
}

Now the value of the linkObj variable will be remembered, and when the event handler is triggered, it will use these 'saved' values, so your script will behave as expected.

And of course change the loop accordingly:

// ...
for(j=0; j < e.length; j++) {
lnk[lnk.length] = e[j];
last = lnk.length-1;
l = lnk[last];
l.id = "button_" + (last<10?"0"+last:last);
obj[last] = new linkClass(l.id,null,0,-288,-72,72,33);
obj[last].formatLink();
//
attachEventHandlers(obj[last]);
//
obj[last].highlight();
obj[last].changeImage();
}
// ...

You can play around with this on FireFox for example, but to make it run on more browsers, you need to change the attachEventHandlers function, for example to make it Internet Explorer compatible, or make it possible to work on old, DOM Level-1 browsers as well.

fside

5:17 am on Mar 8, 2008 (gmt 0)

10+ Year Member



This is actually a problem area, particularly when it comes to IE and their odd attachEvent.

Check out the names, Dean Edwards and Zidjel. There was a contest, recently, for an alternative which could adequately control and remove handlers, in IE.

I found it didn't work so well for 'onscroll', being a global sort of thing to begin with. So I wrote a separate and lone module to handle IE scrolling. All elements get added to that, rather than having events added to assorted elements. All the elements are handled by the one module run by onscroll. But stacking the events to specific elements should work fine, otherwise, as Zidjel (is that right?) proposed, and Edwards too, and through their assorted variations on the same.

But it's a 'can of worms'. You just never know. The simple things can prove to be very time consuming. Who would have thought, attachEvent?

vol7ron

1:22 am on Mar 9, 2008 (gmt 0)

10+ Year Member



gergoe, I will try removing the 'on' keywords.
fside, I have no idea what you are talking about. As much as I would like to help you with your problem, that doesn't belong in this thread. I understand their are differences b/t Firefox and IE, but we primarily use FF at work, so first I want to make it work there, then I'll make it work in IE. Then I'll obfuscate and minimize it.

gergoe, as for the function itself, I am not too keen on variable names or the order. I am just in development and during development/testing, I'm trying lots of different ways. Right now, I have not optimized the logic; there may also be vestigial variables. I'm more concerned with getting it to work and then cleaning up later :)

thx for all your help guys

gergoe

1:39 am on Mar 9, 2008 (gmt 0)

10+ Year Member



Moving the event handler definitions is not only because that looks better, but because it will work then :-)

Try moving it to the function as suggested and see if it works as expected, and after that, you can do some research on Javascript Closures, because that's what you and me were doing with those functions.

fside

2:25 am on Mar 9, 2008 (gmt 0)

10+ Year Member



> I have no idea what you are talking about. As much as I would like to help you with your problem, that doesn't belong in this thread. <

If you have no clue what I'm talking about then you don't know if it was either a problem or if it was germane to your problem. C'mon. Take time to read what you write.

Whether you wish to obfuscate or 'minify', I was saying, and I'll repeat myself, that when you get to IE6, for example, you will be faced with a choice of trying to accomodate the M$ attachEvent design, or adopting an alternative. You can search the names I suggested. They developed - an alternative - to attachEvent, in order to fix the various problems with IE's design.

Any better?

vol7ron

6:18 pm on Mar 9, 2008 (gmt 0)

10+ Year Member



yes fside, that is better. and i understand that there are differences b/t IE and FF with event handlers. The only problem is that I was asking about addEventListener, which is obviously FF, not IE. I made no question about IE or how to fix it in IE.

This is actually a problem area, particularly when it comes to IE
Statement that it's a common problem, thank you.

Check out the names, Dean Edwards and Zidjel. There was a contest, recently, for an alternative which could adequately control and remove handlers, in IE.
Statement that someone else had a solution for IE, thank you.

I found it didn't work so well for 'onscroll', being a global sort of thing to begin with...
Statement that you found a solution through using onscroll, thank you.

Your post still is still fluffy. It just said, "hey you'll have problems with IE, go search for an answer, you can start here." I do appreciate your attempt though, and thank you for taking the time to respond to my problem, so don't misunderstand me, but I feel like you didn't read my original post because you didn't really show me an example or provide a solution.

I guess I was looking for problems/suggestions in the code I wrote above. If you look at gergoe's post that's more of what I was looking for, which I will try when I get back to work tomorrow.

fside

8:31 am on Mar 10, 2008 (gmt 0)

10+ Year Member



> obviously FF, not IE <

I just find it difficult to believe that someone would write off IE support for a public website. And attachEvent poses problems. These guys provided an alternative. I was looking ahead for you. Sorry you didn't understand. I was trying to help.

vol7ron

2:53 pm on Mar 10, 2008 (gmt 0)

10+ Year Member



I know fside, and really I appreciate it! That's why I was still thanking you. If anything, I'm just aggravated and annoyed with myself-

This is something that I should know, I've just been away for too long and am mad that I'm getting old and forgetful. A few months ago I also de-obfuscated Google suggest and added comments. I have several AJAX e-books, but only some have a like-google approach, whereas the code I had was Google's, but clarified. It would have been a prime example because it uses on/over events. Anyhow, I've lost most of the code, but have begun redoing it :( I didn't do it to copy them, only to learn their approach.

Thank you all so much for your help, I really appreciate it and will try to help you where I can. I also do appreciate your IE interjections, fside, because there is no doubt that I will have to make it IE compatible. My concern right now was just for FF because the site is not public, it's for an intranet that primarily uses FF - go figure? However, I do hope to use this knowledge for some of my personal sites in the future.

Thx again,
vol7ron



vol7ron

4:07 pm on Mar 10, 2008 (gmt 0)

10+ Year Member



gergoe,

Removing "on" from the event names, helped out a bit and I understand what you're saying about the objects - without the function this only works for the last rollover, but that's why I tried to tie them to global arrays. With the function, one of the images don't show up and the rollover doesn't work.

I've tried modifying the function from:


function attachEventHandlers(oDiv, oLink, linkObj) {
oDiv.addEventListener("mouseover",function(){linkObj.highlight();} , false);
oLink.addEventListener("mouseout", function(){linkObj.changeImage();}, false);
}

And of course change the loop accordingly:
// ...
for(j=0; j < e.length; j++) {
lnk[lnk.length] = e[j];
last = lnk.length-1;
l = lnk[last];
l.id = "button_" + (last<10?"0"+last:last);
obj[last] = new linkClass(l.id,null,0,-288,-72,72,33);
obj[last].formatLink();
//
attachEventHandlers(obj[last]);
//
obj[last].highlight();
obj[last].changeImage();
}
// ...

To

// call the function
attachEventHandlers(d[i ],e[j],obj[last]);

// function definition
function attachEventHandlers(oDiv, oLink, linkObj) {
oDiv.addEventListener("mouseover",function(){linkObj.highlight();} , false);
oDiv.addEventListener("mouseout", function(){linkObj.changeImage();}, false);
}

Which I messed up at first, but does work! Thank you very much. I do see some delay, which I'm hoping I can fix. But this is awesome and now all of fsides suggestions I'm sure will come into play :)

I still don't fully understand how the separate function changes the scope of the object, or really what is going on there, but I will study it and make modifications as I see fit.

I hope this post will help someone with their multi-state rollovers in the future.


vol7ron




vol7ron

4:57 pm on Mar 10, 2008 (gmt 0)

10+ Year Member



A quick way to make this cross-browser was to:

function attachEventHandlers(oDiv,oLink, linkObj) {
try {
oDiv.addEventListener("mouseover", function(){linkObj.highlight();} , false);
oLink.addEventListener("mouseout", function(){linkObj.changeImage();}, false);
} catch (err) {
oDiv.attachEvent("onmouseover", function(){linkObj.highlight();});
oLink.attachEvent("onmouseout", function(){linkObj.changeImage();});
}
}

fside, I haven't come across any problems with this, I might want to put a browser-sniffer function in there, but the attachEvent works fine for now. Your previous statements made more sense as I approached this. thx

vol7ron

fside

5:43 pm on Mar 10, 2008 (gmt 0)

10+ Year Member



> try { <

That's not the issue. You don't even need try/catch. Just test for the method directly. Scott Andrew (LePera) [jszen.blogspot.com] simple cross-browser function [scottandrew.com]. However:

When you get to the attachEvent branch, "the hidden assumption behind addEvent() is that addEventListener and attachEvent work exactly the same. They don't." . . . "If you don't really understand the difference between addEventListener and attachEvent, stick to the traditional event registration model:", namely one handler per object event, e.g. $("divA").onclick = clickHandler.
QM Problem Identification [quirksmode.org]

I won't rephrase all the considerations, here. I would suggest you look instead at this [therealcrisp.xs4all.nl]. And based on that, here is [dean.edwards.name] Edwards updated alternative to attachEvent, etc.

This is what I was getting at. I just thought you might 'google' it on your own.

vol7ron

8:12 pm on Mar 10, 2008 (gmt 0)

10+ Year Member



I definitely will, I just wasn't at this stage yet. The $(obj).onclick is the old method though and had been replaced by addEventListener().

For my code in FF2/IE6 they both operate seemingly the same, however I do still need to research the memory leaks and what is going on. My first pass I just wanted to make sure everything was possible and have the ability to show a test site, my next pass I'll do some more heavy duty research and make things more efficient.

I appreciate your persistence, fside. Thank you.

- vol7

gergoe

8:17 pm on Mar 10, 2008 (gmt 0)

10+ Year Member



Removing "on" from the event names, helped out a bit and I understand what you're saying about the objects - without the function this only works for the last rollover, but that's why I tried to tie them to global arrays. With the function, one of the images don't show up and the rollover doesn't work.

The code in my post was just plain wrong, I forgot to assign the second two parameters to the function, but you seem to found your way along anyway :-)

I still don't fully understand how the separate function changes the scope of the object, or really what is going on there, but I will study it and make modifications as I see fit.

It does not change the scope, but sets the context. When defining an anonymous function from the global scope, nothing happens. But when you define an anonymous function from within an another function, the values from the stack of that function are saved along the anonymous function, so when you call the just declared function, any variables you are using in that one will be resolved first from the local scope, then followed by the context of the closure (the function where it was declared), and only after that the global scope. But no variables ever resolve to a value what it was at the defining of the code *and* changed afterwards within the same scope. Your approach were correct on using global objects (although "pollutioning" the global scope is something to avoid), but you failed on the last variable which was set to the last link always at the end of the function, and that it the value which will be available to the anonymous function given to the event handler. So when using variables in anonymous functions which are not defined in the local scope, they are never evaluated at the time of declaring the function, but will be evaluated at the time when the function is called.

Anyway, my explaining skills are rather limited, so I'd suggest you to do some research on JavaScript Closures (as I mentioned earlier), and find a better presenter than me ;-)

vol7ron

2:35 pm on Mar 12, 2008 (gmt 0)

10+ Year Member



Disregard this post --- It's too early and I was forgetting to define the units when setting width/height. Thank you.

fside

10:37 pm on Mar 12, 2008 (gmt 0)

10+ Year Member



> I appreciate your persistence, fside. <

No problem. The idea wasn't that the method was faster, just that try/catch wasn't needed. I just got a CCed email from this Filipack guy telling everyone I was trying to 'fool' people by noting the work of Edwards, Zijdel, etc, above. He said no 'experienced programmer' would fall for it, or something. Well, I consider Dean Edwards to be an experienced programmer. And I think it's better to take advice from them. I can't emphasize that, enough. You get too many self-proclaimed 'experts', operating on their own 'authority'. I prefer to point in the direction of those who are on record as having gotten things right, those who know what they're talking about.

vol7ron

2:50 am on Mar 13, 2008 (gmt 0)

10+ Year Member



No problem, don't take his comment to heart, I'm sure it came out wrong.

I have now finished my rollover, which is working beautifully. Now, I'm modifying it to use (5) 1x33 pixel bg images for the animation effect, and a text overlay. I'm also making the code a little more robust so that a user can have the rollover images side-by-side or one-below-the-other.

The major change I included in my HTML/JavaScript was to perform the attachments on load for IE and on DOM finished loading for Firefox. Everything is working flawlessly now. Though, I haven't looked at the one scope issue noted above. I have, however, looked at the differnce with attachEvent and noted the biggest difference being how "this" is interpreted by the window rather than the calling object.

My next step is to include some unloading to free up memory. Though, I believe I saw some memory-leak issues with passing pointers through functions. Argh, so much to do.

Thx again guys.