Finding closest element without jQuery

Javascript

Javascript Problem Overview


I am trying to find the closest element with a specific tag name without jquery. When I click on a <th> I want to get access to the <tbody> for that table. Suggestions? I read about offset but didn't really understand it too much. Should I just use:

Assume th is already set to clicked th element

th.offsetParent.getElementsByTagName('tbody')[0]

Javascript Solutions


Solution 1 - Javascript

Very simple:

el.closest('tbody')

Supported on all browsers except IE.
UPDATE: Edge now support it as well.

No need for jQuery. More over, replacing jQuery's $(this).closest('tbody') with $(this.closest('tbody')) will increase performance, significantly when the element is not found.

Polyfill for IE:

if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;
if (!Element.prototype.closest) Element.prototype.closest = function (selector) {
    var el = this;
    while (el) {
        if (el.matches(selector)) {
            return el;
        }
        el = el.parentElement;
    }
};

Note that there's no return when the element was not found, effectively returning undefined when the closest element was not found.

For more details see: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

Solution 2 - Javascript

Little (very) late to the party, but nonetheless. This should do the trick:

function closest(el, selector) {
	var matchesFn;

	// find vendor prefix
	['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
		if (typeof document.body[fn] == 'function') {
			matchesFn = fn;
			return true;
		}
		return false;
	})

    var parent;

	// traverse parents
	while (el) {
		parent = el.parentElement;
		if (parent && parent[matchesFn](selector)) {
			return parent;
		}
		el = parent;
	}

	return null;
}

Solution 3 - Javascript

Here's how you get the closest element by tag name without jQuery:

function getClosest(el, tag) {
  // this is necessary since nodeName is always in upper case
  tag = tag.toUpperCase();
  do {
    if (el.nodeName === tag) {
      // tag name is found! let's return it. :)
      return el;
    }
  } while (el = el.parentNode);
  
  // not found :(
  return null;
}

getClosest(th, 'tbody');

Solution 4 - Javascript

There exists a standardised function to do this: Element.closest. Most browsers except IE11 support it (details by caniuse.com). The MDN docs also include a polyfill in case you have to target older browsers.

To find the closest tbody parent given a th you could do:

th.closest('tbody');

In case you want to write the function yourself - here is what I came up with:

function findClosestParent (startElement, fn) {
  var parent = startElement.parentElement;
  if (!parent) return undefined;
  return fn(parent) ? parent : findClosestParent(parent, fn);
}

To find the closest parent by tag name you could use it like this:

findClosestParent(x, element => return element.tagName === "SECTION");

Solution 5 - Javascript

function closest(el, sel) {
	if (el != null)
		return el.matches(sel) ? el 
			: (el.querySelector(sel) 
				|| closest(el.parentNode, sel));
}

This solution uses some of the more recent features of the HTML 5 spec, and using this on older/incompatible browsers (read: Internet Explorer) will require a polyfill.

Element.prototype.matches = (Element.prototype.matches || Element.prototype.mozMatchesSelector 
	|| Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector 
	|| Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector);

Solution 6 - Javascript

To extend @SalmanPK answer

it will allow to use node as selector, useful when you working with events like mouseover.

function closest(el, selector) {
    if (typeof selector === 'string') {
        matches = el.webkitMatchesSelector ? 'webkitMatchesSelector' : (el.msMatchesSelector ? 'msMatchesSelector' : 'matches');
        while (el.parentElement) {
            if (el[matches](selector)) {
                return el
            };
            el = el.parentElement;
        }
    } else {
        while (el.parentElement) {
            if (el === selector) {
                return el
            };
            el = el.parentElement;
        }
    }

    return null;
}

Solution 7 - Javascript

Here's the simple function I am using:-

function closest(el, selector) {
    var matches = el.webkitMatchesSelector ? 'webkitMatchesSelector' : (el.msMatchesSelector ? 'msMatchesSelector' : 'matches');

    while (el.parentElement) {
        if (el[matches](selector)) return el;

        el = el.parentElement;
    }

    return null;
}

Solution 8 - Javascript

Summary:

For finding a particular ancestor we can use:

Element.closest();

This function takes a CSS selector string as an argument. it then returns the closest ancestor of the current element (or the element itself) which matches the CSS selector which was passed in the arguments. If there is no ancestor it will return null.

Example:

const child = document.querySelector('.child');
// select the child

console.dir(child.closest('.parent').className);
// check if there is any ancestor called parent

<div class="parent">
  <div></div>
  <div>
    <div></div>
    <div class="child"></div>
  </div>
</div>

Solution 9 - Javascript

Get closest DOM element up the tree that contains a class, ID, data attribute, or tag. Includes the element itself. Supported back to IE6.

var getClosest = function (elem, selector) {

    var firstChar = selector.charAt(0);

    // Get closest match
    for ( ; elem && elem !== document; elem = elem.parentNode ) {

        // If selector is a class
        if ( firstChar === '.' ) {
            if ( elem.classList.contains( selector.substr(1) ) ) {
                return elem;
            }
        }

        // If selector is an ID
        if ( firstChar === '#' ) {
            if ( elem.id === selector.substr(1) ) {
                return elem;
            }
        } 

        // If selector is a data attribute
        if ( firstChar === '[' ) {
            if ( elem.hasAttribute( selector.substr(1, selector.length - 2) ) ) {
                return elem;
            }
        }

        // If selector is a tag
        if ( elem.tagName.toLowerCase() === selector ) {
            return elem;
        }

    }

    return false;

};

var elem = document.querySelector('#some-element');
var closest = getClosest(elem, '.some-class');
var closestLink = getClosest(elem, 'a');
var closestExcludingElement = getClosest(elem.parentNode, '.some-class');

Solution 10 - Javascript

Find nearest Elements childNodes.

closest:function(el, selector,userMatchFn) {
var matchesFn;

// find vendor prefix
['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
    if (typeof document.body[fn] == 'function') {
        matchesFn = fn;
        return true;
    }
    return false;
});
function findInChilds(el){
    if(!el) return false;
    if(el && el[matchesFn] && el[matchesFn](selector)
    
    && userMatchFn(el) ) return [el];
    var resultAsArr=[];
    if(el.childNodes && el.childNodes.length){
        for(var i=0;i< el.childNodes.length;i++)
        {
             var child=el.childNodes[i];
             var resultForChild=findInChilds(child);
            if(resultForChild instanceof Array){
                for(var j=0;j<resultForChild.length;j++)
                {
                    resultAsArr.push(resultForChild[j]);
                }
            } 
        }
         
    }
    return resultAsArr.length?resultAsArr: false;
}

var parent;
if(!userMatchFn || arguments.length==2) userMatchFn=function(){return true;}
while (el) {
    parent = el.parentElement;
    result=findInChilds(parent);
    if (result)     return result;
    
    el = parent;
}

return null;

}

Solution 11 - Javascript

Here.

function findNearest(el, tag) {
    while( el && el.tagName && el.tagName !== tag.toUpperCase()) {
        el = el.nextSibling;     
    } return el;
} 

Only finds siblings further down the tree. Use previousSibling to go the other way Or use variables to traverse both ways and return whichever is found first. You get the general idea, but if you want to traverse through parentNodes or children if a sibling doesn't match you may as-well use jQuery. At that point it's easily worth it.

Solution 12 - Javascript

A little late to the party, but as I was passing by and just answer back a very similar question, I drop here my solution - we can say it's the JQuery closest() approach, but in plain good ol' JavaScript.

It doesn't need any pollyfills and it's older browsers, and IE (:-) ) friendly: https://stackoverflow.com/a/48726873/2816279

Solution 13 - Javascript

I think The easiest code to catch with jquery closest:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
    $(document).ready(function () {
        $(".add").on("click", function () {
            var v = $(this).closest(".division").find("input[name='roll']").val();
            alert(v);
        });
    });
</script>
<?php

for ($i = 1; $i <= 5; $i++) {
    echo'<div class = "division">'
        . '<form method="POST" action="">'
        . '<p><input type="number" name="roll" placeholder="Enter Roll"></p>'
        . '<p><input type="button" class="add" name = "submit" value = "Click"></p>'
        . '</form></div>';
}
?>

Thanks much.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhuntercView Question on Stackoverflow
Solution 1 - JavascriptoriadamView Answer on Stackoverflow
Solution 2 - JavascriptAlesView Answer on Stackoverflow
Solution 3 - JavascriptJoonView Answer on Stackoverflow
Solution 4 - Javascriptdavid1995View Answer on Stackoverflow
Solution 5 - JavascriptMatthew James DavisView Answer on Stackoverflow
Solution 6 - Javascriptzb'View Answer on Stackoverflow
Solution 7 - JavascriptSalman von AbbasView Answer on Stackoverflow
Solution 8 - JavascriptWillem van der VeenView Answer on Stackoverflow
Solution 9 - JavascriptChris FerdinandiView Answer on Stackoverflow
Solution 10 - JavascriptMajidTaheriView Answer on Stackoverflow
Solution 11 - Javascriptuser1596138View Answer on Stackoverflow
Solution 12 - JavascriptPedro FerreiraView Answer on Stackoverflow
Solution 13 - JavascriptAL MaMunView Answer on Stackoverflow