Kajiyama VK
Kajiyama VK

Reputation: 454

event.stopPropagation() not working - Capturing still passes on function

First of all - I'm a noob. Sorry if this question is basic enough. Second - I tried to search and as I can see I applied the solution as instructed in other posts.

With that being said... I have three sets of DIVs. Each set consists on 2 Divs(one inside another). On the OUTER div there's a function that is saying to make a border if it's clicked.

To see the error:

  1. Click on the word OUTER.
  2. Click on the word INNER.

The code is suppose to make a border only in the outer.

HTML

<html>
    <head>
        <title>Event Propagation</title>
        <meta charset="UTF-8" />

    </head>
    <body onload="onLoad()">

        <!--Squares 1-->
        <div style="display:inline-block">
            <div class="outer" style="height: 100px; width: 100px;">
                Outer
                <div class="inner" style="height: 50px; width: 50px;margin:20px"> 
                    Inner
                </div>
            </div>
        </div>
 <!--Squares 2-->
        <div style="display:inline-block">
            <div class="outer" style="height: 100px; width: 100px;">
                Outer
                <div class="inner" style="height: 50px; width: 50px;margin:20px"> 
                    Inner
                </div>
            </div>
        </div>

         <!--Squares 3-->
         <div style="display:inline-block">
            <div class="outer" style="height: 100px; width: 100px;">
                Outer
                <div class="inner" style="height: 50px; width: 50px;margin:20px"> 
                    Inner
                </div>
            </div>
        </div>


    </body>
</html>

The JS

    <script>
        function onLoad(){
            
            const elOuter = document.querySelectorAll(".outer")

            elOuter.forEach(element=>{
                element.addEventListener('click', fnChangeColor,false)
            })
        }

        function fnChangeColor(e){

            e.target.style.border="1px solid black"

            if (e.stopPropagation) {
  
                e.stopPropagation()
            } else {
  
                e.cancelBubble = true
            }
        }

    </script>

FYI - I solved the question creating events to the children attributing the e.stopPropagation on them. That worked. But I know that's not the best approach. If I have a div with 100 divs inside, that'll be a hell on earth.

Upvotes: 4

Views: 3135

Answers (2)

Kajiyama VK
Kajiyama VK

Reputation: 454

In this case we have one outer div - the parent, and the inner div - the children.

So - the issue to understand here is:

target: Who triggered the event. currentTarget: the element that has the function set in.

When clicking the inner div, you are clicking the children - but the element that has the function is the parent. So - the function should change the currentTarget (the outer div), not the target (that's the inner div that was clicked).

Just had to change

e.target.style.border="1px solid black"

with

e.currentTarget.style.border="1px solid black"

Upvotes: 0

Mister Jojo
Mister Jojo

Reputation: 22265

this is because when you click on "Inner" e.target element is "Inner", not "Outer".

What you do seem to me to a try of understanding event delegation, do it this way:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>xxx</title>
  <style>
    body > div { display:inline-block; }
    .outer     { height: 100px; width: 100px; background: lightgrey; }    
    .inner     { height: 50px; width: 50px;margin:20px; background: lightblue; }
    .bordering { border: 1px solid black; }
  </style>
</head>
<body>
   <div> <div class="outer">  Outer <div class="inner">  Inner </div> </div> </div>
   <div> <div class="outer">  Outer <div class="inner">  Inner </div> </div> </div>
   <div> <div class="outer">  Outer <div class="inner">  Inner </div> </div> </div>
<script>
document.body.onclick= e => {
  if (!e.target.matches('.outer')) return // ignore other click events
  e.target.classList.toggle('bordering') 
  }
</script>
</body>
</html>

or this way

document.querySelectorAll('.outer').forEach(el=>{
  el.onclick=e=>{

    // console.log(e.target.className)
    if (!e.target.matches('.outer')) return // ignore other click events
    e.target.classList.toggle('bordering') 
  }
})

In your code, there is only one event used and no way for any propagation.

when the user click on a .inner the event append on the .inner element an never on the .outer.
it's like a bubble following it's a bottom up way, not the reverse. when the bubble finds a target code (an avent listener), there is no more propagation and the bubble burst.

If you want to see an event propagation you have to set 2 event listeners ( an that mean 2 bubles).

You can see that by adding a console.log(e.target.className) like here:

document.querySelectorAll(".outer, .inner") // => 6 event listeners
  .forEach(el=>{ el.addEventListener('click', getClick , false) })
 
var counter = 0

function getClick(e)
  {
  console.log( `${++counter} - event listener is on : ${e.currentTarget.className}
      clicked element is : ${e.target.className} `)
  }
  
cClear.onclick=_=>{ counter = 0; console.clear() }
body > div { display:inline-block; }
.outer     { height: 100px; width: 100px; background: lightgrey; }    
.inner     { height: 50px; width: 50px;margin:20px; background: lightblue; }
<div> <div class="outer"> Outer <div class="inner"> Inner </div> </div> </div>
<div> <div class="outer"> Outer <div class="inner"> Inner </div> </div> </div>
<div> <div class="outer"> Outer <div class="inner"> Inner </div> </div> </div>
<button id="cClear">clear console</button>

Upvotes: 3

Related Questions