将click事件绑定到嵌套for循环中创建的元素

时间:2014-12-17 05:27:06

标签: javascript events dom nested-loops

我今天一直在关闭关闭的很多,并且已经查看了很多类似的问题。我还没有能找到一个触及我遇到的完全相同的问题,以及如何修复它。大多数答案似乎与jQuery有关,但关于闭包如何工作有一些很棒的答案。我知道我的问题在哪里。我尝试了许多不同的闭包示例而没有运气。

我有一组代表3个校园的数据。每个校区都有一些建筑物。第一个for循环遍布校园。嵌套for循环遍历当前园区中的建筑物并将点击事件绑定到它(这允许我以编程方式平移和缩放传单地图。)

我创建了一个非常简化的JSFiddle来代表我尝试做的事情,这表明了这个问题。只有最后一个校园的建筑物#34;组保留点击事件。

这是来自JS Fiddle的JS代码。 http://jsfiddle.net/scwutpno/3/

var campuses = {
    "campus1": {
        "buildings": {
            "building1": {
                "id": 1
            },
                "building2": {
                "id": 2
            },
                "building3": {
                "id": 3
            }
        },
        "name": "Campus 1"
    },
        "campus2": {
        "buildings": {
            "building1": {
                "id": 4
            },
                "building2": {
                "id": 5
            },
                "building3": {
                "id": 6
            }
        },
        "name": "Campus 2"
    },
        "campus3": {
        "buildings": {
            "building1": {
                "id": 7
            },
                "building2": {
                "id": 8
            },
                "building3": {
                "id": 9
            }
        },
        "name": "Campus 3"
    }
};

var buildingLinksContainer = document.getElementById("building-list");

function buildExternalLinks() {

    for (var campus in campuses) {
        var container,
        item;


        if (buildingLinksContainer !== null) {
            buildingLinksContainer.innerHTML += '<div class="building-links" id="' + campus + '-buildings">' +
                '<div class="building-links__campus-name">' + campuses[campus].name + '</div></div>';

            container = document.getElementById(campus + '-buildings');
        }


        for (var building in campuses[campus].buildings) {

            item = container.appendChild(document.createElement('li'));
            item.innerHTML = 'Building ' + campuses[campus].buildings[building].id;

            item.addEventListener("click", function () {
                alert('clicked!');
            });
        }

    }
}

buildExternalLinks();

我已尝试将点击事件置于自我调用的关闭状态,但我不认为里面的数据是点击事件。有问题。它是应用click事件的节点。我不知道如果因为我使用嵌套for循环,我需要某种嵌套闭包概念吗?

很想知道这个问题是如何解决的。我已经通过在for循环中使用闭包来克服它一次,但是这个嵌套for循环让我感到难过。

3 个答案:

答案 0 :(得分:1)

问题在于,每次附加到&#34; buildingLinksContainer&#34;时,您的代码都会重写DOM。元件。每当您的代码附加到&#34; buildingLinksContainer&#34;的内部HTML与&#34; + =&#34;时,DOM就是从头开始创建的。以前在其中包含事件监听器的所有元素&#34; buildingLinksContainer&#34;被创建为没有任何事件监听器的新元素。

仅在最后一次迭代中,当DOM未被拆除后,事件监听器才会坚持下去。并且由于在最后一次迭代中只创建了最后三个li元素,因此只有它们具有事件侦听器。

罪魁祸首:

buildingLinksContainer.innerHTML += '<div class="building-links" id="' + campus + '-buildings">' +
'<div class="building-links__campus-name">' + campuses[campus].name + '</div></div>';

我已经以一种有效的方式重写了您的解决方案(获得更好的性能和标记奖励)

var buildingLinksContainer = document.getElementById("building-list");

function buildExternalLinks(campuses, rootElement) {

    var ul = document.createElement('ul'),
        li = document.createElement('li'),
        h1 = document.createElement('h1'),
        list = document.createDocumentFragment();

    for(var campus in campuses) {
        if(campuses.hasOwnProperty(campus)) {
            var c = ul.cloneNode(false);
            c.classList.add('building-links');
            c.setAttribute('id', campus+'-buildings');

            var name = h1.cloneNode(false);
            name.appendChild(document.createTextNode(campuses[campus].name));

            c.appendChild(name);

            for(var building in campuses[campus].buildings) {
                if(campuses[campus].buildings.hasOwnProperty(building)) {
                    var b = li.cloneNode(false);
                    var text = 'Building ' + campuses[campus].buildings[building].id;
                    b.appendChild(document.createTextNode(text));
                    b.addEventListener('click', function(event) {
                        alert(event.target.innerHTML);
                    });

                    c.appendChild(b);
                }
            }
        }

        list.appendChild(c);
    }

    rootElement.appendChild(list);
}

buildExternalLinks(campuses, buildingLinksContainer);

我还更新/分发了您的JSFiddle

答案 1 :(得分:0)

+=只是x = x + y之类的快捷方式。设置buildingLinksContainer.innerHTML += ...时,您实际上会重新创建该元素的整个内容。 全部删除了之前的内容,包括附加到元素的事件。然后一个新的字符串被设置为DOM,并且在解析它时,不再有任何事件。

快速解决方法是使用insertAdjacentHTML()代替innerHTML,如下所示:

buildingLinksContainer.insertAdjacentHTML('beforeend',
    '<div class="building-links" id="' + campus + '-buildings">' +
    '<div class="building-links__campus-name">' +
    campuses[campus].name +
    '</div></div>'
);

A live demo at jsFiddle

答案 2 :(得分:0)

使用事件委派仅将事件侦听器添加到 building-list 元素

document.querySelector('#building-list').addEventListener('click', function(evt){
   var clickedElement = evt.target;
   //just use the clickedElement variable to get the element that was clicked
   //ex: you can get the id from it or whatever you want
});