Determine closest ancestor that has a certain HTML class When writing JavaScript code for my own projects, this functionality is the singlemost used algorithm that does not already exist: Finding an ancestor element that has a certain HTML class.

Algorithm:


Does the current element have a parent element?


Yes: Continue with step 2
No: There are no solutions to the task, return a null value

Does the parent element of the current element have the required class?


Yes: Return the parent element
No: Set the current element to its parent element and repeat step 1



Code:

Element.prototype.getAncestorByClassName = function(className) {
    var currentParent = this.parentElement
    while (true) {
        // If the root of the DOM is reached, an ancestor cannot be found anymore.
        if (currentParent === null) {
            return null
        }

        if (currentParent.classList.contains(className)) {
            return currentParent
        }

        var currentParent = currentParent.parentElement
    }
}


Notes:


I want to be able to call element.getAncestorByClassName("class-name") hence the use of Element.prototype. Does this approach yield any problems? I am aware that I would override any getAncestorByClassName() functions that are introduced in future versions of ECMAScript
There are possibly multiple results for the task I specified. The algorithm will return the closest ancestor that has the given class.
If there are no results, null is returned as other functions like getElementById() do the same.
Browser support should cover all modern browsers. I do not consider IE8–IE10 modern browsers. The lack of support for classList on these is noted but not essential to me right now.
Extending native prototypes is a little iffy. Not that this should cause any problems, but in principle it's best to avoid. The reason is mostly that if everyone extend native prototypes, then you're more likely to run into issues (i.e. if you include a library that tramples on your extensions or vice-versa). Hence the general consensus is to try to leave them alone.

All that said, the main point approach it with open eyes, and proceed at your own risk.

In this case, I doubt you risk much, but it's always a good idea to consider something like:

if (!Element.prototype.getAncestorByClassName) {
  // extend the prototype
}


just to only do the extension if the function doesn't already exist. Of course, this is considerably more useful for polyfills, where a standardised function does exist and its signature is known. Here, if a getAncestorByClassName function already exists, it's hard to know if it works the same as yours since either one would be non-standard. Anyway, just something to consider.

As for the code:

The first step you take is to call currentNode.parentElement. This precludes you finding the element itself. Granted the function is called get ancestor, but still... say you call it on an element like body &gt; p &gt; span.highlight, and ask it to find the class highlight: You'd get null back. I'd include the initial element in the search.

Your also doing some unnecessary work in checking for === null inside the loop, since the while loop's condition could do that. Since you always end up exiting the loop somehow, it might as well be apparent in the loop's own condition, rather than have it be explicitly defined as an infinite loop.

And there's no reason to redeclare var currentNode inside the loop; it's already declared.

Also not a fan of the "minimal semicolons"-style, but that's just me. Works without them too.

Cleaned up a bit, it could look like:

Element.prototype.getAncestorByClassName = function(className) {
    var currentElement = this;

    while (currentElement) {
        if (currentElement.classList.contains(className)) {
            return currentElement;
        }
        currentElement = currentElement.parentElement;
    }

    return null;
}


Of course, since you're extending the Element prototype you could be clever and make it recursive. It looks nice, but I'd probably stick to the simpler loop myself:

Element.prototype.getAncestorByClassName = function(className) {
    if (this.classList.contains(className)) {
        return this;
    }

    if (!this.parentElement) {
        return null;
    }

    return this.parentElement.getAncestorByClassName(className);
}