Event Delegation, how to target a specific span inside a div

6.2k views Asked by At

Given a bunch of divs like these:

<div class="itemGrid">
    <div id="itemOne" class="fruit">
       <span> Banana </span>
          
        </div>

     <div id="itemTwo" class="fruit">
        <span> Apple </span>
          
      </div>
</div>

I want to be able, using event delegation, to get the value of the span (so just the text) when I click on those divs.

With this I get everything, the text AND the symbol, but I can't seem to be able to just get the text inside the span:

const fruitButtons = document.querySelectorAll(".fruit span");
const grid = document.querySelector(".itemGrid");

grid.addEventListener("click", function(e) {
  if (e.target.nodeName == "DIV") {
    console.log(e.target.textContent)
  }
})
3

There are 3 answers

4
Scott Marcus On BEST ANSWER

Reworking the scenario a little:

const fruitButtons = document.querySelectorAll(".fruit > span");
const gridItems = document.querySelectorAll(".fruit");

// Loop through the div.fruit elements:
gridItems.forEach(function(div){
  
  // Wire each div.fruit to a click handler:
  div.addEventListener("click", function(e){
    // When clicked, look for the first span within the div and get its text
    console.log(div.querySelector("span").textContent);
  }); 
});
<div class="itemGrid">
    <div id="itemOne" class="fruit">
       <span> Banana </span>
          
     </div>

     <div id="itemTwo" class="fruit">
        <span> Apple </span>
          
      </div>
</div>

2
Sᴀᴍ Onᴇᴌᴀ On

Use getElementsByTagName() to find the span elements under the target.

Edit:

As was pointed out, one could interpret an implicit requirement that the user should be able to click on the span elements and have the contents of that element logged. Support for this has been added by adding the while loop to search up the chain of ancestors:

//search ancestors for div in case user clicks on span element
while (target.nodeName !== "DIV" && e.target.parentNode !== null) {
    target = e.target.parentNode;
}

const fruitButtons = document.querySelectorAll(".fruit span");
const grid = document.querySelector(".itemGrid");

grid.addEventListener("click", function(e) {
  var target = e.target;
  //search ancestors for div in case user clicks on span element
  while (target.nodeName !== "DIV" && e.target.parentNode !== null) {
      target = e.target.parentNode;
  }
  if (target.nodeName == "DIV") {
    var spans = target.getElementsByTagName('span');
    if (spans.length) {
      console.log(spans[0].textContent);
    }
  }
})
<div class="itemGrid">
  <div id="itemOne" class="fruit">
    <span> Banana </span>
    
  </div>

  <div id="itemTwo" class="fruit">
    <span> Apple </span>
    
  </div>
</div>

1
pwolaq On

Your code is not a proper event delegation

You should check if you clicked inside div with class=fruit. Then you can extract text from its span descendant.

grid.addEventListener("click", function(e) {
    let node = e.target;
    let div = null;

    // first let's check if we clicked inside div with class fruit by going up the DOM tree
    while(node !== this){
        if(node.hasClass("fruit")){
            div = node;
            break;
        }

        node = node.parentNode;
    }

    // if we clicked inside, log the value of span
    if(div !== null){
        console.log(div.querySelector("span").textContent);
    }
});