How to write a jQuery like library in 71 lines of code — Learn about the DOM

Kurt
Kurt
Feb 11, 2016 · 8 min read

Developers are neglecting learning the DOM itself

The DOM, or Document Object Model, is the heart and soul of you web browser . You’re looking at it right now. To clarify, the DOM is not a feature of JavaScript — its not even written in JavaScript — it is a programming interface, a API between language and browser. Language controls calculations etc, browser controls display and events.

Getting Started

We need to create our base object. Let’s call it domElement. This object will act as a wrapper for elements being targeted.

var domElement = function(selector) {
this.selector = selector || null; //The selector being targeted
this.element = null; //The actual DOM element
};

Now we can start adding functionality.

The jQuery methods we will replicate are the selector/creator $() .on(), .off(), .val(), .append, .prepend() and .html()

domElement.prototype.eventHandler = {
events: [] //Array of events & callbacks the element is subscribed to.
}
domElement.prototype.eventHandler = {
events: [], //Array of events the element is subscribed to.
bindEvent: function(event, callback, targetElement) {
//remove any duplicate event
this.unbindEvent(event,targetElement);

//bind event listener to DOM element
targetElement.addEventListener(event, callback, false);
this.events.push({
type: event,
event: callback,
target: targetElement
}); //push the new event into our events array.
}
}

That’s it! Lets break the function down quickly

  1. We remove any existing events on the element that have the the type that is being bound. This is purely a matter of personal preference. I prefer to keep singular event handlers, since they’re easier to manage and debug. Removing the line will allow multiple handlers of the same type. We will create the unbindEvent function a little later.
  2. We bind the event to the DOM Element, making it live.
  3. We push the event and all its info into the events array so the element can keep track of our listeners.
domElement.prototype.eventHandler = {
events: [], //Array of events the element is subscribed to.
bindEvent: function(event, callback, targetElement) {
//remove any duplicate event
this.unbindEvent(event,targetElement);

//bind event listener to DOM element
targetElement.addEventListener(event, callback, false);
this.events.push({
type: event,
event: callback,
target: targetElement
}); //push the new event into our events array.
},
findEvent: function(event) {
return this.events.filter(function(evt) {
return (evt.type === event); //if event type is a match return
}, event)[0];
}
}

Now we can add our unbindEvent method.

domElement.prototype.eventHandler = {
events: [], //Array of events the element is subscribed to.
bindEvent: function(event, callback, targetElement) {
//remove any duplicate event
this.unbindEvent(event,targetElement);

//bind event listener to DOM element
targetElement.addEventListener(event, callback, false);
this.events.push({
type: event,
event: callback,
target: targetElement
}); //push the new event into our events array.
},
findEvent: function(event) {
return this.events.filter(function(evt) {
return (evt.type === event); //if event type is a match return
}, event)[0];
},
unbindEvent: function(event, targetElement) {
//search events
var foundEvent = this.findEvent(event);
//remove event listener if found
if (foundEvent !== undefined) {
targetElement.removeEventListener(event, foundEvent.event, false);
}
//update the events array
this.events = this.events.filter(function(evt) {
return (evt.type !== event);
}, event);
}
};

And that’s our event handler! Try it out below…

domElement.prototype.on = function(event, callback) {
this.eventHandler.bindEvent(event, callback, this.element);
}
domElement.prototype.off = function(event) {
this.eventHandler.unbindEvent(event, this.element);
}
domElement.prototype.val = function(newVal) {
return (newVal !== undefined ? this.element.value = newVal : this.element.value);
};
domElement.prototype.append = function(html) {
this.element.innerHTML = this.element.innerHTML + html;
};
domElement.prototype.prepend = function(html) {
this.element.innerHTML = html + this.element.innerHTML;
};
domElement.prototype.html = function(html) {
if(html === undefined){
return this.element.innerHTML;
}
this.element.innerHTML = html;
};

Initialization

On initialization we need to do one of two things…

  1. If the selector starts with an open bracket ‘<’ we will create a new element.
  2. Otherwise we will use the document.querySelector to select an existing element.
domElement.prototype.init = function() {
switch(this.selector[0]){
case ‘<’ :
//create element
var matches = this.selector.match(/<([\w-]*)>/);
if(matches === null || matches === undefined){
throw ‘Invalid Selector / Node’;
return false;
}
var nodeName = matches[0].replace(‘<’,’’).replace(‘>’,’’);
this.element = document.createElement(nodeName);
break;
default :
this.element = document.querySelector(this.selector);
}
};

Lets walk through the above code.

  1. We use a switch statement, and pass the first character of our selector as the argument.
  2. If it begins with a bracket, we do a quick Regex match to find the text between the the open and close brackets. If this fails we throw an error that the selector is invalid.
  3. If a match is made, we strip out the brackets and pass the text to document.createElement to create a new element.
  4. Alternatively, we look for a match using document.querySelector this returns null if there is no match found.
  5. Lastly, we set the element property on our domElement to the matched / created element.

Using $ to reference domElement

Lastly we will assign the $ symbol to initialize a new domElement.

$ = function(selector){
var el = new domElement(selector); // new domElement
el.init(); // initialize the domElement
return el; //return the domELement
}

Here’s a pen running the complete library…use your console.

What to do next?

  1. Why not try to replicate your favorite utility functions?
  2. Dive into the DOM
  3. Use event listeners to bind two-way data.

Important Notes

A special thanks to Quincy Larson for humanizing this post by fixing my butchery of the English language, visual tweaks and the great header image.

var domElement = function(selector) {
this.selector = selector || null;
this.element = null;
};
domElement.prototype.init = function() {
switch (this.selector[0]) {
case ‘<’:
var matches = this.selector.match(/<([\w-]*)>/);
if (matches === null || matches === undefined) {
throw ‘Invalid Selector / Node’;
return false;
}
var nodeName = matches[0].replace(‘<’, ‘’).replace(‘>’, ‘’);
this.element = document.createElement(nodeName);
break;
default:
this.element = document.querySelector(this.selector);
}
};
domElement.prototype.on = function(event, callback) {
var evt = this.eventHandler.bindEvent(event, callback, this.element);
}
domElement.prototype.off = function(event) {
var evt = this.eventHandler.unbindEvent(event, this.element);
}
domElement.prototype.val = function(newVal) {
return (newVal !== undefined ? this.element.value = newVal : this.element.value);
};
domElement.prototype.append = function(html) {
this.element.innerHTML = this.element.innerHTML + html;
};
domElement.prototype.prepend = function(html) {
this.element.innerHTML = html + this.element.innerHTML;
};
domElement.prototype.html = function(html) {
if (html === undefined) {
return this.element.innerHTML;
}
this.element.innerHTML = html;
};
domElement.prototype.eventHandler = {
events: [],
bindEvent: function(event, callback, targetElement) {
this.unbindEvent(event, targetElement);
targetElement.addEventListener(event, callback, false);
this.events.push({
type: event,
event: callback,
target: targetElement
});
},
findEvent: function(event) {
return this.events.filter(function(evt) {
return (evt.type === event);
}, event)[0];
},
unbindEvent: function(event, targetElement) {
var foundEvent = this.findEvent(event);
if (foundEvent !== undefined) {
targetElement.removeEventListener(event, foundEvent.event, false);
}
this.events = this.events.filter(function(evt) {
return (evt.type !== event);
}, event);
}
};
$ = function(selector) {
var el = new domElement(selector);
el.init();
return el;
}

freeCodeCamp.org

This is no longer updated. Go to https://freecodecamp.org/news instead

Kurt

Written by

Kurt

I learnt to program using tutorials on the internet, so to pay tribute I write about programming on the internet.

freeCodeCamp.org

This is no longer updated. Go to https://freecodecamp.org/news instead