Givi
Givi

Reputation: 1734

JavaScript Event Delegation - Behavior

I'm trying to create web-page, and to improve performance, I decide to use event delegation, instead of direct binding, but I came across with strange behavior of event delegation or I missed something...

Here is structure of my HTML

<div>
    <section class="myClass" data-link="section 1">
        <h1>Heading 1</h1>
        <p>Some text....</p>
    </section>
</div>

I want to entire section was clickable, but while clicking h1 or p element in section event.target on that moment is h1 or p and not section, so expression in if statement fails...

function delegate(ele) {
    ele.parentNode.addEventListener("click", function (e) {
        if (e.target.nodeName.toLowerCase() === "section" && e.target.classList.contains("myClass")) {
            console.log("delegate " + e.target.getAttribute("data-link"));
        }
    }, false);
}

I already have solution with direct binding, but it would be good is achieved in the same result with event delegation. Here is jsFiddle

function direct(ele) {
    [].forEach.call(ele, function (value, index, array) {
        value.addEventListener("click", function(e) {
            console.log("direct " + value.getAttribute("data-link"));
        }, false);
    });
}

Thanks in advance!

Upvotes: 2

Views: 627

Answers (3)

Givi
Givi

Reputation: 1734

I found the solution with CSS pointer-events

jsFiddle with CSS


.myClass * {
    pointer-events: none;
}

or with JavaScript

jsFiddle with JavaScript


(function () {
    "use strict";

    var ele = document.querySelector(".myClass").parentNode;

    delegate(ele);

    function delegate(ele) {
        ele.addEventListener("click", function (e) {
            var target = e.target;

            while (!(target.nodeName.toLowerCase() === "section" && target.classList.contains("myClass"))) {
                target = target.parentNode;
                if (target === ele) {
                    break;
                }
            }

            if (target.nodeName.toLowerCase() === "section" && target.classList.contains("myClass")) {
                console.log("delegate " + target.getAttribute("data-link"));
            }
        });
    }
}());

Upvotes: 2

Oriol
Oriol

Reputation: 287980

Add the event listener to ele instead of ele.parentNode, and use this instead of e.target.

Demo

function delegate(ele) {
    ele.addEventListener("click", function (e) {
        if (this.nodeName.toLowerCase() === "section" && this.classList.contains("myClass")) {
            console.log("delegate " + this.getAttribute("data-link"));
        }
    }, false);
}

Be aware that e.target is the element the event was dispatched on. But in your case you don't want that, you want the element which has the event handler, so use this (or ele).

Upvotes: 1

GameAlchemist
GameAlchemist

Reputation: 19294

Once you handled the click, you can walk the visual tree, starting from the target, to find the information you need.
You might seek by type (node name), by tag, ... for the sake of the example, i just seek on element up to get a 'data-link' attribute if i don't find one on the target, but you have many choices here.

Edit : another idea is to use event.currentTarget, to get the element on which you hooked the event.

The updated fiddle will print :

delegate call on h1 or p of section 1 

when you click on h1 or p1

and it will print :

delegate section 1 

when you click on the whole section.

http://jsfiddle.net/KMJnA/4/

function delegate(ele) {
    ele.parentNode.addEventListener("click", delegateHandler, false);
}

function delegateHandler (e) {
    var target = e.target;  
    var attribute = target.getAttribute("data-link");
    // if target has no attribute
    // seek it on its parent
    if (!attribute) {
         attribute = target.parentNode.getAttribute("data-link");
    }

    if ( target.classList.contains("myClass") ) { 
         console.log("delegate " + attribute);
        } 
    else console.log('delegate call on h1 or p of ' + attribute);
}    

Rq : i didn't get why you hook the event on the parent of the element, might be simpler not to do it. :-)

Upvotes: 3

Related Questions