loading...
Cover image for Writing a .parents() function using Vanilla JS

Writing a .parents() function using Vanilla JS

urielbitton profile image Uriel Bitton Updated on ・4 min read

Intro

Moving away from the super useful and powerful Jquery in order to use more modern frameworks (such as React, Vue and Ember JS), the thing i miss the most about jquery was its super simple DOM manipulation that we don't have with Vanilla JS.

My top favorite built in functions were the .parents() function and the .on() event handlers (the $ elm saved so many charactersof code!). These 2 functions single handedly reduced Vanilla JS code by a factor of 10x at least. The .on() function in jquery let us bind an event handler such as click to any number of elements as easy as:

$('p').on("click", function() {
    //execute anything when any of the p tags on the page are clicked
}

That was super cool and useful. Now with Vanila JS we know we must loop through the collection of p tags and bind click events to each item in that collection (as they are treated as an array).
The code is much more complex:

const p = document.querySelectorAll('p')
for(item of p) {
    p.onclick = () => { //ES6
        //execute actions when p tags are clicked
}
}

As we can see its doable but adds easily 3-4 lines of extra code (even using ES6)

But for the magical .parents() function, Javascript does not even provide us a long or short way to achieve what jquery did. So let's see what we need to design in order to simulate the .parents() behaviour. In the end it'll add almost 20 lines of extra code as opposed to just writing .parents() but we'll have a readily available function that we can reuse whenever we want! Its gonna be a little long but stick around I promise you won't regret it.

function getParents(el, parentSelector) {
    if (parentSelector === undefined) {
        parentSelector = document;
    }
    var parents = [];
    var p = el.parentNode;
    while (p !== parentSelector) {
        var o = p;
        parents.push(o);
        p = o.parentNode;
    }
    parents.push(parentSelector);
    return parents;
}

Explainging the Code

Let's understand this code. The first line initiates the function and passes two parameters, the actual element which we are at and an optional parent selector to stop our parent search at.

The second line says if we dont provide a parent selector then set the parent selector as the root-most element, which is the document itself (above the html tag).

Then we create a parent array and stuff all of the parent elements of our element. We then have a while loop that dictates that while the current parent node is not our provided parent selector (or document) then assign that node to a variable o and push o into the parents array.
So every iteration where that parent node is not the provided parent selector, we add that node to an array. Eventually our provided parent selector will be the current parent node as it goes up and compares to every parent node all the way up the DOM.
Once there is a match, the while loop will stop, the array will finally push our provided parent selector and thus the highest parent node will be at the end of the array. Finally we return the array parents and we are done.

Now how do we actually make use of our custom built function to use like .parents() in jquery? Simple. We call the function on the element we want and pass as arguments our element and a max parent selector like so:

//parentDiv is our max parent selector
const button = document.querySelector('button')
const parentDiv = document.querySelector('.parentdiv')

//here we call our getParents function
const parents = getParents(button, parentDiv)
//and here the magic happens, since our parent node is always the last one
parents[parents.length-1] 

Since our parent selector is always the last one we can refer to it simply by using our parents array and retrieving the last element in the array by doing parents.length-1 (will get the last element in the array).

We can then do

parents[parents.length-1].querySelector('p').innerHTML //finds a p tag's inner html contents inside our parents element, just like in jquery

Comparing Jquery to Vanilla JS

Finally lets see the comparision between our custom built Vanilla JS solution and jquery's solution:

//jquery
var mytxt = $('button').parents('.parent1').find('p').html() //will get us the html content of a p element inside the parent div of our button - super useful DOM manipulation

//Vanilla JS
const parents = getParents(button, parent1)//assuming button and parent1 are already assigned 
const mytxt = parents[parents.length-1].querySelector('p').innerHTML

And now we have our desired result inside the mytxt const using Vanilla JS instead of Jquery!

Final Thoughts

Sure the Vanilla JS solution required quite some extensive code but ultimately its not that bad and well worth to use if we reuse the function across our app.

Thanks for taking interest in my post and reading up to the end. I hope you will find this useful and that it helps you in your future coding projects!

Posted on by:

urielbitton profile

Uriel Bitton

@urielbitton

Hi i'm an avid web and app developer in search for creativity and always going after new ideas. Developer of Reactor JS

Discussion

markdown guide
 

Great article ...

All in all if you want pure DOM manipulation (not using React or Vue or whatever) then still nothing beats the jQuery API for elegance and conciseness.

Makes me wonder if there isn't something like a "jQuery lite" which wraps the Vanilla API and provides a small subset of the most useful/popular jQuery functions, but with a minimal bundle size (the full jQuery library is still surprisingly large) ... you've written part of it already.

 

Don’t forget that web servers will compress files before transferring to client (usually gzipped), which practically means that the size of a text file will be around 1/3 - 1/4 or even less than its original size.

 

Well yes that's always the case when server gzip is enabled, regardless the type of resource (JS, CSS) or whether you use jQuery or another library, but having a smaller bundle size is beneficial anyway. I was just a little bit surprised when looking at the bundle size of jQuery and realizing that it isn't really much smaller than more full-featured libs like React or Vue.

 

Thanks!

Youre right a jquery lite could be a good idea.

 

Something like that might even exist already, previously there was something called zepto.js ... it still "exists" but latest version is from 2016 (and nobody uses it anymore I think) ... then there's"jquery-lite" (github.com/deager/jquery-lite) which is 5 years old and very limited ... no those 2 aren't going to win the war.

"jQuery Slim" looks better, but still large ... then this one, "Cash":

github.com/fabiospampinato/cash

I think that's what we would be looking for - almost all of the stuff you need and want from jQuery (including "parent()", "closest()", "append()", "on()", "off()", you name it), but the download is 3 to 4 times smaller.

Next question is, does it perform equally well as jQuery ... if it downloads quicker but execution is sluggish then you haven't gained anything.

interesting i'll have a look at these thanks.

But i'm anyways working on enhancing my vanilla js skills so im gonna try to get as good with JS as i am with jquery and i wont even think about the convenience jquery has in a few months :)