Event Bubbling vs Event Capture

Small example showing the differences between event bubbling and event capture.

by Couto

HTML

<link rel="stylesheet" href="https://rawgithub.com/necolas/normalize.css/master/normalize.css">
<link rel="stylesheet" href="http://topcoat.io/topcoat/css/topcoat-desktop-dark.css">
<div class="example"> First Element 
    <div class="example"> Second Element 
        <div class="example"> Third Element </div>
    </div>
</div>
<button class="topcoat-button--cta">Currently using Bubbling</button>
<button class="topcoat-button">Reset Counters</button>

CSS

.example {
    padding: 15px; margin: 15px;
    font: 12px 1.5 "Helvetica Neue", Helvetica, Arial;
    font-weight: bold;
    border: 1px solid #bbb;
    color: #444;
    background-color: white;
    position: relative;
    cursor: pointer;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-font-smoothing: antialiased;
}

button {
    margin: 0 25px;
}

.example:hover { 
    background-color: #efefef; 
    cursor: pointer;
}

.example:active { 
    background-color: #ccc; 
    border: 1px solid #999;
    cursor: pointer;
}

.badge {
    position: absolute;
    bottom: 7px;
    right: 7px;   
}

JavaScript

(function(d) {

    var divs = d.querySelectorAll('div'),
        button = d.querySelectorAll('button'),
        counter = 1,
        capture = false,

        callback = function(evt) {
            var el = evt.currentTarget,
                badge = d.createElement('span');
    
            badge.setAttribute('class', 'badge badge-inverse');
            badge.innerHTML = counter;
                
            el.appendChild(badge);
            
            counter += 1;
        },

        removeBadges = function() {
            var badges = d.querySelectorAll('.badge'),
                i = badges.length - 1;
            
            while (i >= 0) {
                badges[i].parentNode.removeChild(badges[i]);
                i -= 1;
            }

            counter = 1;
        },

        removeListeners = function() {
            var i = divs.length - 1;

            while (i >= 0) {
                divs[i].removeEventListener('click', callback, capture);
                i -= 1;
            }
        },

        addListeners = function() {
            var i = divs.length - 1;

            while (i >= 0) {
                divs[i].addEventListener('click', callback, capture);
                i -= 1;
            }
        };

    addListeners();

    button[0].addEventListener('click', function(evt) {
        
        removeBadges();        
        removeListeners();
        capture = !capture;
        
        addListeners();

        if (capture) {
            this.innerHTML = 'Currently using Capture';
        } else {
            this.innerHTML = 'Currently using Bubbling';
        }
    });

    button[1].addEventListener('click', removeBadges);

}(document));