JSFiddle - React, Tailwind, and code Playground

by Vincent Maglione

HTML

<p>
  <strong>Event phase for clicked boxes</strong><br>
  <label for="clickRad1">Bubble (default)</label>
  <input type="radio" name="boxPhase" value="false" id="clickRad1" checked />

  <label for="clickRad2">Capture</label>
  <input type="radio" name="boxPhase" value="true" id="clickRad2" />
</p>

<p>
  <strong>Event phase for <span style="color:papayawhip;">#content</span></strong><br>
  <label for="clickRad3">Bubble (default)</label>
  <input type="radio" name="contentPhase" value="false" id="clickRad3" checked />

  <label for="clickRad4">Capture</label>
  <input type="radio" name="contentPhase" value="true" id="clickRad4" />
</p>
  
<main id="content">
  
  <div id="b1" class="box">
  1
    <div id="b2" class="box">
    2
      <div id="b3" class="box">
        3
      </div>
    </div>
  </div>
</main>

<footer>
  <button id="clearMsg">
    Clear
  </button>
  <p id="clickMsg">
    No clicks
  </p>
</footer>

SCSS

* {
  box-sizing: border-box;
}

#content {
  background: papayawhip;
  border: 1px solid rgba(255, 0, 0, 0.5);
  padding: 1em;
  width: 100%;
}

.box {
  border: 1px solid rgba(0,0,0,0.2);
  padding: 2em;
  text-align: center;
  width: 100%;
  
  &:hover {
    background: rgba(0,0,0,0.1);
  }
}

JavaScript

var content = document.getElementById('content'),
    boxes = document.getElementsByClassName('box'),
    clickMsg = document.getElementById('clickMsg'),
    msg = '',
    boxPhase = false,
    contentPhase = false,
    boxCfg = document.querySelectorAll('input[name=boxPhase]'),
    contentCfg = document.querySelectorAll('input[name=contentPhase]');

var checkPhase = function(phase) {
	if (1 === phase) {
	  return 'Capturing';
  } else if (2 === phase) {
    return 'At Target';
  } else if (3 === phase) {
    return 'Bubbling';
  }
  
  return 'No phase';
}

var handleClicks = function(e) {
  clickMsg.innerHTML = msg;
  var phase = e.eventPhase;
  
  msg += e.target.id.toString() + ' phase: ' + checkPhase(phase) + '<br>';
  
  clickMsg.innerHTML = msg;
}

var handleBubbles = function(e) {
  var phase = e.eventPhase;
  msg += '<strong>#content</strong> heard event from ' + e.target.id.toString() + ' via ' + checkPhase(phase) + '<br>';
  
  clickMsg.innerHTML = msg;
}

var setupListeners = function(els) {
	for (var i = els.length - 1; i > -1; i--) {
    var el = els[i];
    el.addEventListener('click', handleClicks, boxPhase);
  }
  
  content.addEventListener('click', handleBubbles, contentPhase)
}(boxes);

var getChecked = function(els) {
  let l = els.length;
  
  if (l) {
    for (let i = 0; i < l; i++) {
      return els[i].checked ? false : true
    }
  }
  
  return false
}

var resetListeners = function(els, fn, phase) {
  let l = els.length;
  
  if (l) {
    for (var i = 0; i < l; i++) {
      els[i].removeEventListener('click', fn, !phase);
      els[i].addEventListener('click', fn, phase);
    }  
  } else {
    els.removeEventListener('click', fn, !phase);
    els.addEventListener('click', fn, phase);  
  }
}

var updateBoxPhase = function(els) {
  boxPhase = getChecked(boxCfg);
  resetListeners(boxes, handleClicks, boxPhase);
} 

var updateContentPhase = function(els) {
  contentPhase = getChecked(contentCfg);
  resetListeners(content, handleBubbles,...