JavaScript Event Delegation without jQuery
jQuery does lots of cool stuff, but it isn't necessary every time. You can get a surprising amount of stuff done with the event object.
by Brian Cribb
HTML
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<div class="container narrow">
<h1>Delegate Without jQuery</h1>
<p class="lead">Enter some text into the input and click the green button. After adding a list item, delete it with the corresponding red button.</p>
<p>No, <strong>do not</strong> just hit <code>Enter</code>. There's no keyboard stuff in this fiddle, so just use the green button.</p>
<form class="pos-relative">
<div class="form-group stuff-wrapper">
<label class="sr-only" for="stuff">stuff to add</label>
<input id="input-stuff" type="stuff" class="form-control stuff-input" id="stuff" placeholder="Enter stuff">
</div>
<button id="btn-add" type="button" class="btn btn-success btn-action-add" aria-label="Add">
<span class="glyphicon glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
</form>
<div class="panel panel-primary">
<!-- Default panel contents -->
<div class="panel-heading">List of Stuff</div>
<!-- List group -->
<ul id="stuff-list" class="list-group"></ul>
</div>
</div>
CSS
.narrow {
max-width: 500px;
}
.pos-relative {
position: relative;
}
.stuff-wrapper {
max-width:100%;
margin-right:50px;
}
.btn-action-add {
position: absolute;
top:0;
right:0;
}
.btn-action-subtract {
position: absolute;
top:4px;
right:4px;
padding: 4px 9px;
}
.btn:focus {
outline: none;
}
/*
This last bit is important. We want the click events to ignore the span
inside the button so the event delegation will be easier. This way the layout
still works okay, but the clicks start on the button.
*/
.glyphicon {
pointer-events: none;
}
JavaScript
// Starting with a Crockford-style IIFE - http://benalman.com/news/2010/11/immediately-invoked-function-expression/
(function startDelegating(){
// Caching these thingies to variables so we can use them bunches of lots.
var stuffList = document.getElementById('stuff-list'),
btnAdd = document.getElementById('btn-add'),
listInput = document.getElementById('input-stuff');
// Adding our listeners the vanilla way.
btnAdd.addEventListener('click', plusHandler);
stuffList.addEventListener('click', minusHandler);
// Plus handler: read the input to see if it has stuff. If so, add a list element with that stuff in it.
function plusHandler(event) {
if ( listInput.value.length > 0 ) {
// We have some nested things, so we're using a fragment.
var listFragment = document.createDocumentFragment(),
li = document.createElement('li'),
btn = document.createElement('button'),
span = document.createElement('span');
// Adding classes to our new elements.
li.className = 'list-group-item';
btn.className = 'btn btn-danger btn-action-subtract pull-right';
span.className = 'glyphicon glyphicon glyphicon-minus';
// Setting the content to match the stuff in the input.
li.textContent = listInput.value;
// Assembling our elements to make a Bootstrap button in a Bootstrap list group item
// inside a Bootstrap panel. Bootstrap.
btn.appendChild(span);
li.appendChild(btn);
listFragment.appendChild(li);
// Add that nested mess to the main ul element all in one go! Yay document fragments!
stuffList.appendChild(listFragment);
} else {
// Do nothing because there was no stuff in the input.
console.log('plusHandler(): input was empty.');
};
}
// Minus handler:
function minusHandler(event) {
// Do stuff if the user clicked on the BUTTON, not the LI.
if(event.srcElement.tagName === 'BUTTON') {
var targetNode = event.target.parentElement;
console.log(event); // Look at this in the console....