<div>
  <div>
    <div>
      <div >
        <div>
          <div>
            <div>
              <div>
                <div>
                    <div id='target'>click me!!</div>   
                </div>
                
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
div {
  padding: 1.5em 0 0 1em;
  background-color: rgba(0,0,0,0.2);
  position: relative;
  font-family: monospace;
  
  &::before {
    position: absolute;
    content: '<div>';
    left: 1em;
    top:0;
  }
  
  &::after {
    content: 'bubble';
    left: 5em;
    position: absolute;
    opacity: 0;
    top: 0;
  }
  
  
  &.capturing {
    background-color: rgba(253, 206, 171, 0.5);
    
    &::after {
      content: 'capturing';
      opacity: 1;
    }
  }
  
  &.bubbling {
    background-color: rgba(232, 74, 95, 0.5);
    
    &::after {
      content: 'bubbling';
      opacity: 1;
    }
  }
}

#target {
  background-color: #99b898;
  padding: 0.2em;
  display: block;
  text-align: center;
  
  &::before {
    content: '#target'
  }
}
View Compiled
/*

  Just click on "click me!!" and see three phases in action!!!

  - event capturing
  - event target
  - event bubbling

  for more information on event propagation here's some reference https://www.sitepoint.com/event-bubbling-javascript/

  ps. The code used here is only for visualization purpose, so there's no capture / bubble argument needed.
*/
const divs = document.getElementsByTagName('div');
const targetElement = document.getElementById('target')
const speed = 200;
let ancestorElement = targetElement.parentNode;
let distance = 1;
while (ancestorElement && ancestorElement.tagName === 'DIV') {
  let currentDistance = distance;
  console.log(ancestorElement, (currentDistance + 1) * speed);
  ancestorElement.addEventListener('click', (e) => {

    const targetElement = document.getElementById('target')
    const currentTarget = e.currentTarget
    if (e.target === targetElement) {

      setTimeout(() => {
        currentTarget.classList.add('capturing')
        setTimeout(() => {
          currentTarget.classList.remove('capturing')
        }, speed)
      }, (MAX_DISTANCE - currentDistance) * speed + 1)

      
      setTimeout(() => {
        currentTarget.classList.add('bubbling')
        setTimeout(() => {
          currentTarget.classList.remove('bubbling')
        }, speed)
      }, (MAX_DISTANCE + currentDistance + 4) * speed)
    }

  })

  ancestorElement = ancestorElement.parentElement;
  distance++;
}

MAX_DISTANCE = distance;
targetElement.addEventListener('click', () => {
  setTimeout(() => {
    targetElement.innerHTML = "TARGET PHASE!!!"
  
    setTimeout(() => {
      targetElement.innerHTML = "&nbsp;"
      setTimeout(() => {
        targetElement.innerHTML = "click me!!"
      }, MAX_DISTANCE * speed )
    }, speed * 4)

    
  }, MAX_DISTANCE * speed)
})



External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.