Opposite to jQuery's .Closest (Top/Far-Most?)
Asked Answered
E

4

22

I have a code with lots of submenus that share the same class name.

Here's a structure:

.menu
  .sub-menu
  .sub-menu
    .sub-menu
    .sub-menu
  .sub-menu
    .sub-menu
      .elem
      .elem
  .sub-menu

Note that .sub-menu may be infinite levels deep.

So how do I achieve this: when .elem is clicked, I want to travers the DOM upwards until the top-most .sub-menu is reached and apply a style to it. I am aware of .closest() and .parent() and .find(), but I have no idea if jQuery has such feature such as .topMost(selector)?

The only way I can think of is maybe running a loop and going through .closest('.sub-menu') of the new element until its length is zero (there are no more parents with this class, so it must be the top-most). However, I think there should be a more practical approach to this.

Eggett answered 9/2, 2013 at 11:27 Comment(0)
T
36

Assuming by 'opposite of closest' you want the 'furthest' parent element, you can use parents().last(), like this:

$('.elem').on('click', e => {
  var $topSubMenu = $(e.target).parents('.sub-menu').last();
});

Note, you want the last element in the array as jQuery traverses up the DOM, so the top-level item will the final one in the collection.

If instead you mean that you want the 'closest' child element, then you can use find('.submenu').first(), like this:

$('.elem').on('click', e => {
  var $topSubMenu = $(e.target).find('.submenu').first();
});

You could potentially use children('.submenu') here instead of find() - it depends how many levels down the DOM tree you need to traverse.

Tirewoman answered 9/2, 2013 at 11:29 Comment(0)
B
33

The question is slightly misleading as .closest() could be misinterpreted. Actually it means search up the tree until the first match is found. So the opposite is search down the tree until a match is found and that method is first().

To find all descendants one might use find(). The opposite of find() is parents().

closest() (all levels) For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.

first() (all levels) Reduce the set of matched elements to the first in the set

parents() (all levels) Get the ancestors of each element in the current set of matched elements, optionally filtered by a selector.

parent() (next level only) Get the parent of each element in the current set of matched elements, optionally filtered by a selector.

find() (all levels) Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.

Beriosova answered 13/6, 2013 at 18:44 Comment(4)
Feel free to edit the title to a more suitable one if you think it will clarify the purpose of the question for future readers.Eggett
Im cool with it. Its what i googled. Just posted it for future reference :)Beriosova
This is incorrect with regard to find() being the opposite of closest(). find() will find all elements in the tree. So the opposite of closest(), searching down, would be find().first().Cognizable
Cool! Took away the down vote and up voted instead. This should be helpful to others coming upon this post.Cognizable
D
4

Here's a JavaScript-only solution, based on the polyfill for closest().

function furthest(s) {
    var el = this.parentElement || this.parentNode;
    var anc = null;

    while (el !== null && el.nodeType === 1) {
        if (el.matches(s)) anc = el;
        el = el.parentElement || el.parentNode;
    }
    return anc;
}
Delimitate answered 20/7, 2019 at 10:37 Comment(1)
I added element as a parameter because this.parentElement doesn't workMadi
B
0

.closest() could actually be used here, by passing a selector that restricts the matched elements to those which are direct descendants of .menu:

$('.elem').click(function() {
    let $topSubMenu = $(this).closest('.menu > .sub-menu');
});
Batfish answered 11/6, 2021 at 12:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.