-->

applying an event handler to newly created objects

2020-05-05 16:52发布

问题:

So my goal is to have 5 boxes and every time one box is clicked a new box appears. The code I wrote for that is this:

window.onload = function(){
    var boxList = document.getElementsByClassName("box");
    for(i = 0; i< boxList.length;i++){
    boxList[i].onclick = clickHandler;
    } 
}

function clickHandler(eo){
    if(eo.target.style.backgroundColor != "black") {
        eo.target.style.backgroundColor = "black";
        var box = document.createElement("div");
        box.setAttribute("class","box");
        box.setAttribute("id",document.getElementsByClassName("box").length++);
        document.getElementById("Master").appendChild(box);
    }
    else eo.target.style.backgroundColor = "white";
}

The class of all the divs is "box" and I just add a new id to every new box. My problem is that the event handler doesn't seem to work for the newly created boxes. How could that be solved?

Many thanks in advance!

回答1:

box.onclick = clickHandler;

There are more elegant ways, but as that's what you're already doing, there's no harm doing what you're doing, now.

In a different world, you might do something like:

var master = document.querySelector("#master");

master.addEventListener("click", clickHandler);

function clickHandler (e) {
  var box = e.target;
  var newBox;
  var totalBoxes = master.querySelectorAll(".box").length;
  if (!box.classList.contains("box")) {
    return; // not a box
  }

  if (isBlack(box)) {
    changeColour(box, "white");
  } else {
    newBox = makeNewBox(totalBoxes + 1);
    master.appendChild(newBox);
    changeColour(box, "black");
  }
}

I don't have to worry about further click-handling beyond that, if all of the boxes are descendants of #master. makeNewBox here is simply separating the creation of the object from what you actually want to do with it.



回答2:

You will need to add an onclick event to your newly added box.

box.onclick = clickHandler;



回答3:

If you create boxes dynamically after the window.onload handler has already run, then you will have to run some additional code on those dynamically created boxes that assigns the click handler to them after they have been created.

function clickHandler(eo){
    if(eo.target.style.backgroundColor != "black") {
        eo.target.style.backgroundColor = "black";
        var box = document.createElement("div");
        box.setAttribute("class","box");

        // add this line of code to assign the click handler
        box.onclick = clickHandler;

        box.setAttribute("id",document.getElementsByClassName("box").length++);
        document.getElementById("Master").appendChild(box);
    }
    else eo.target.style.backgroundColor = "white";
}

Or, you can use delegated event handling and handle the events from a common parent that is not dynamically created.

Delegated event handling uses "event bubbling" where events bubble up their parent chain so you can attach a click handler to a common parent and then check e.target in that click handler to see if the click occurred on one of your box elements and then process it one place. In cases of dynamically added content, this can work very well.

Delegated event handling in your code would look something like this:

window.onload = function(){
    // put click handler on common box parent and use event bubbling
    document.getElementById("Master").addEventListener("click", clickHandler);
}

function clickHandler(eo){
    // if this click occurred on one of my boxes
    if (hasClass(eo.target, "box"))
        if(eo.target.style.backgroundColor != "black") {
            eo.target.style.backgroundColor = "black";
            var box = document.createElement("div");
            box.setAttribute("class","box");
            box.setAttribute("id",document.getElementsByClassName("box").length++);
            document.getElementById("Master").appendChild(box);
        }
        else eo.target.style.backgroundColor = "white";
    }
}

// utility function for checking a class name
// could also use .classList with a polyfill for older browsers
function hasClass(elem, cls) {
    var str = " " + elem.className + " ";
    var testCls = " " + cls + " ";
    return(str.indexOf(testCls) !== -1) ;
}