Onclick event in nested list

A

André Hänsel

Hi,

I have a nested list:
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li id="sublist">Sublist
<ul>
<li>Sublink 1</li>
<li>Sublink 2</li>
</ul>
</li>
</ul>

I have set an event handler on the "sublist" LI that toggles the
visibility of the contained UL.

But now when someone clicks Sublink 1 or 2 the UL is hidden, because
the UL is in the LI and so the LI receives the event. How can I
prevent this?

I tried setting an event handler on the innermost LI's (with Sublink 1
and 2) and calling stopPropagation() from there but that did not even
work. However it would be an ugly way.

Regards,
André
 
D

David Mark

Hi,

I have a nested list:
<ul>
        <li>Link 1</li>
        <li>Link 2</li>
        <li id="sublist">Sublist
                <ul>
                        <li>Sublink 1</li>
                        <li>Sublink 2</li>
                </ul>
        </li>
</ul>
Okay.


I have set an event handler on the "sublist" LI that toggles the
visibility of the contained UL.
Where?


But now when someone clicks Sublink 1 or 2 the UL is hidden, because
the UL is in the LI and so the LI receives the event. How can I
prevent this?

In your case, attach a listener to the outer list. Don't do anything
unless the ID of the target element is "sublist" (and assume your
design will evolve.)
I tried setting an event handler on the innermost LI's (with Sublink 1
and 2) and calling stopPropagation() from there but that did not even
work.

What did you expect that to do?
However it would be an ugly way.

No argument there. Leave bubbling alone.
 
T

Thomas 'PointedEars' Lahn

André Hänsel said:
I have a nested list:
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li id="sublist">Sublist

(You don't need that ID.)
<ul>
<li>Sublink 1</li>
<li>Sublink 2</li>
</ul>
</li>
</ul>

I have set an event handler on the "sublist" LI that toggles the
visibility of the contained UL.

But now when someone clicks Sublink 1 or 2 the UL is hidden, because
the UL is in the LI and so the LI receives the event. How can I
prevent this?

In handleClick(o, e) called as

<li onclick="if (typeof event != "undefined") handleClick(this, event)">

act only if (e.target || e.srcElement) == o.
I tried setting an event handler on the innermost LI's (with Sublink 1
and 2) and calling stopPropagation() from there but that did not even
work. However it would be an ugly way.

I suggest to ignore David in this case. You seldom need (or want)
Event::stopPropagation() (and equivalents!), and you don't need (or want) it
here. Event bubbling works fine if you know how to handle it.

That said, your design might be borken according to accessibility standards.
If yes, use :hover plus a scripted equivalent (.className or .style) instead.


PointedEars
 
D

David Mark

(You don't need that ID.)




In handleClick(o, e) called as

  <li onclick="if (typeof event != "undefined") handleClick(this, event)">

act only if (e.target || e.srcElement) == o.


I suggest to ignore David in this case.  You seldom need (or want)
Event::stopPropagation() (and equivalents!), and you don't need (or want)it
here.  Event bubbling works fine if you know how to handle it.

Odd that you suggest he ignore me and then agree with me 100%. My
meaning was to leave the bubbling to the browser (e.g. never stop it.)
That said, your design might be borken according to accessibility standards.

Hard to say from what I've seen of it.
 If yes, use :hover plus a scripted equivalent (.className or .style) instead.

I suggest he ignore that.
 
T

Thomas 'PointedEars' Lahn

David said:
Odd that you suggest he ignore me and then agree with me 100%. My
meaning was to leave the bubbling to the browser (e.g. never stop it.)

Yes, I misread.
Hard to say from what I've seen of it.

It can be presumed from what we have seen of it.
I suggest he ignore that.

Why?


PointedEars
 
J

Jorge

(...)
But now when someone clicks Sublink 1 or 2 the UL is hidden, because
the UL is in the LI and so the LI receives the event. How can I
prevent this?
(...)

(function () {
var sublist= document.getElementById('sublist');

sublist.onclick= function (e) {
e= e || window.event;
if ((e.target || e.srcElement) === sublist) {

//your code here

}
};
})();

(Not tested)
 
G

Gabriel Gilini

[snip]

(function () {
  var sublist= document.getElementById('sublist');

  sublist.onclick= function (e) {
    e= e || window.event;
    if ((e.target || e.srcElement) === sublist) {
^^^^^^^
leaks in ie6
 
J

Jorge

                                         ^^^^^^^
                                     leaks in ie6

Hmm, I wonder if it isn't time already to stop masking IE's multiple
bugs at our cost but in M$'s benefit, and instead start telling the
users about the true nature of IE: "IE is such a piece of crap, it's M
$'s fault, although they've got plenty of time (years) to fix it they
haven't -only God knows why-. You'd better switch to decent browser,
thanks.". Anyways...

(function () {

function gEBI (id) {
return document.getElementById(id);
}

gEBI('sublist').onclick= function (e) {

if (((e= e || window.event).target || e.srcElement) === gEBI
('sublist')) {

//your code here

}
e= null;
};
})();

Still leaks ?
 
T

Thomas 'PointedEars' Lahn

(For the nth time: )

Use this instead:

if (!e) e = window.event;

Although that is not sufficient because we are dealing with host objects
here and must not jump to conclusions about anything (in that line of
thinking one must also reconsider my previous suggestion:

var t = e.target || e.srcElement;

Thinking about the number of places where one would have to implement
these fallbacks, I am inclined to write the following only once:

/* jsx.object is defined before as an augmented Object */
jsx.dom.getEvent = function(e) {
if (e)
{
var result = e;
}
else if (typeof window.event != "undefined")
{
result = window.event;
}

return result;
};

jsx.dom.getEventTarget = function(e) {
if (typeof e.target != "undefined")
{
var result = e.target;
}
else if (typeof e.srcElement != "undefined")
{
result = e.srcElement;
}

return result;
};

And then:

function ...(ev)
{
var dom = jsx.dom;

var e = dom.getEvent(ev);
if (e)
{
var t = dom.getEventTarget(e);
if (t)
{
// ...
}
}
}
^^^^^^^
leaks in ie6

Correct, using `this' instead is better.


PointedEars
 
J

Jorge

Correct, using `this' instead is better.
(...)

If you can trust in -this- in IE... (?):

document.getElementById('sublist').onclick= function (e) {

if (((e= e || window.event).target || e.srcElement) === this) {

//your code here

}
e= null;
};

Still leaks ?
 
D

David Mark

Hmm, I wonder if it isn't time already to stop masking IE's multiple
bugs at our cost but in M$'s benefit, and instead start telling the
users about the true nature of IE: "IE is such a piece of crap, it's M
$'s fault, although they've got plenty of time (years) to fix it they
haven't -only God knows why-. You'd better switch to decent browser,
thanks.". Anyways...

No, it is time for you to gain some semblance of understanding about
this bug. It's not hard and you are walking right into it in this
case. Use the ID (for this example.) Normally it would be a class as
the parent nodes need different styling and you are likely to have
more than one of them.

In any event. Do not create circular references with host objects.
And leave the users alone.
(function () {

  function gEBI (id) {
    return document.getElementById(id);
  }

  gEBI('sublist').onclick= function (e) {

    if (((e= e || window.event).target || e.srcElement) === gEBI
('sublist')) {

      //your code here

    }
    e= null;
  };

})();

Still leaks ?

No, but it's a really stupid design.
 
D

David Mark

If you can trust in -this- in IE... (?):

Why are you guessing? This issue is simple and very well documented.
document.getElementById('sublist').onclick= function (e) {

  if (((e= e || window.event).target || e.srcElement) === this)

Don't assign in conditions.

{
    //your code here

  }
  e= null;

};

Still leaks ?

No and you don't need to set - e - to null. Tou do need to check for
text nodes though (use the parentNode in that case.)
 
J

Jorge

No and you don't need to set - e - to null.

Are you sure ? DOM_Node.onevent -> function_object.[[scope]] ->
scope_chain -> Activation_object.nodeRef -> DOM_Node is as circular
for var e as it was for var sublist in the outer f() in the previous
code... ?
You do need to check for
text nodes though (use the parentNode in that case.)

Why, if "this" can't be !== gEBI("sublist") ?
 
D

David Mark

No and you don't need to set - e - to null.

Are you sure ? DOM_Node.onevent -> function_object.[[scope]] ->
scope_chain -> Activation_object.nodeRef -> DOM_Node is as circular
for var e as it was for var sublist in the outer f() in the previous
code... ?

Strangely, your diagram does not include an event object. If you
think there is a similar diagram that proves you need to set e to
null, you are mistaken. Leave it alone.

[snip]
 
J

Jorge

Strangely, your diagram does not include an event object.  If you
think there is a similar diagram that proves you need to set e to
null, you are mistaken.  Leave it alone.

Ok, but would you tell me how is var e different than var sublist when
both point to the sublist DOMElement ?
 
D

David Mark

Looks like it doesn't leak anymore:

I'd look again. First off, MS is often wrong. Secondly, I know IE6
still leaks. And, of course, you have no idea what security patches
will be present on any given Windows installation an
"Almost the same changes were also shipped with the latest security
updates of IE6. So, the previous code does not actually leak anymore"

http://blogs.msdn.com/gpde/pages/javascript-memory-leak-detector.aspx

Easier to assume they are wrong.
 
D

David Mark

Ok, but would you tell me how is var e different than var sublist when
both point to the sublist DOMElement ?

e = e || window.event;

Does that look like a DOM node to you?
 
R

RobG

Set the listener as:

<li><span onclick="...">Sublist</span>

See below for why.
(You don't need that ID.)




In handleClick(o, e) called as

  <li onclick="if (typeof event != "undefined") handleClick(this, event)">

act only if (e.target || e.srcElement) == o.

It should be noted that that will fail if the LI has child elements
before the child UL, e.g.

<li ...><b>Ipsum</b> <span class="foo">lorem</span>
<ul>...


It may be a better strategy to wrap the text part of the li in a span
and attach the listener to that, then it will work as requried without
needing to detect where the click came from. The listener just needs
to go up to the parent LI of the EventTarget, e.g.

<li><span onclick="..."><b>Ipsum</b> <span class="foo">lorem</span></
span>
<ul>...


Bubbling is left free to do its thing. :)
 
R

RobG

Looks like it doesn't leak anymore:

"Almost the same changes were also shipped with the latest security
updates of IE6. So, the previous code does not actually leak anymore"

http://blogs.msdn.com/gpde/pages/javascript-memory-leak-detector.aspx

I think that is complete bullshit. I just tested IE 6 on Windows XP
SP3 and it leaks.

IE easily swallowed over 2GB of memory that wasn't released until all
IE windows were closed, even if the window with the leaky page was
closed or navigated to a differnt URL. The article is from early
2008, XP SP 3 was released in April 2008, well after the article.

For the record, the IE version I used is 6.0.2900.5512.xpsp_sp3_gdr.
080814-1236.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,782
Latest member
ThomasGex

Latest Threads

Top