Is there a way to implement jQuery.closest() using DOM manipulation or pure JavaScript?

Here is the HTML I am attempting to target. Given this HTML structure:

<table class="non-unique-identifier table">
<tr><td><div id="unique-identifier"></div></td></tr>
</table>

I am trying to select #unique-identifier:

var myDiv = document.getElementById('#unique-identifier');

Next, I want to select the table element. However, I would like to avoid making the code overly dependent so that it does not require multiple parent nodes to be referenced:

var myDiv = document.getElementById('#unique-identifier'),
    myTable = myDiv.parentNode.parentNode.parentNode.parentNode;

The Question at Hand

Is there a DOM equivalent of jQuery's $().closest() method available? An efficient closest implementation that does not rely on nested for loops is preferable.

Restrictions

For this particular issue, I am restricted from using jQuery or sizzle and introducing new libraries. The codebase has been around for quite some time which leads to these limitations and the continued use of <tables>.

Answer №1

If you're looking to achieve this task without the use of a loop, it can still be done through recursion. Here's an alternative approach:

function closest(el, predicate) {
  return predicate(el) ? el : (
     el && closest(el.parentNode, predicate)
  );
}

Here's a practical demonstration that incorporates Sizzle for DOM queries:

// s = selectors
// n = number of selectors
// get closest s[i+1] from s[i]
// where 0 <= i < n and i % 2 = 0

function main (s) {
  var i, el, from;
  var n = s.length;
  for (i = 0; i < n; i += 2) {
    from = Sizzle(s[i])[0];
    el = closest(from, function (el) {
      return !!el && el !== document && (
        Sizzle.matchesSelector(el, s[i + 1])
      );
    });
    console.log(el);
  }
}

function closest(el, predicate) {
  do if (predicate(el)) return el;
  while (el = el && el.parentNode);
}

main([
  "#winner", "b",
  "#winner", "p",
  "#winner", "div",
  "#winner", "div:not(#trump)",
  "#winner", "#clinton",
  "#loser", "html"
]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/sizzle/1.10.18/sizzle.min.js"></script>

<div id="main">
  <div id="trump">
    <p>Donald <b id="winner">Trump</b></p>
  </div>
  <div id="clinton">
    <p>Hillary <b>Clinton</b></p>
  </div>
</div>

Answer №2

If you want to include an updated response, now there is a useful method called

Element.closest(<query_selector>)
.

Visit this link for more information about Element.closest

Please note that this feature may not work on Internet Explorer, but the Mozilla documentation provides a polyfill code for supporting IE8 and IE9+.

Answer №3

An efficient and concise solution for finding the closest element using any CSS selector is demonstrated below, tested with Benchmark.js:

var el = Element.prototype;
el.matches = el.matches || el.webkitMatchesSelector || el.msMatchesSelector || el.mozMatchesSelector;

function findClosestElement( elem, selector ) {
    while (elem !== document.body) {
        elem = elem.parentElement;
        if (elem.matches(selector)) return elem;
    }
}

This code snippet is compatible with IE9+ and all major modern browsers.

Answer №4

function findClosestId(element, identifier) {
  while (element.id !== identifier) {
    element = element.parentNode;
    if (!element) {
      return null;
    }
  }
  return element;
}

// Example of usage:

targetElement = findClosestId(document.getElementById('unique-identifier'),'targetId')
alert(targetElement.id);
<div id="targetId">
  Finish
  <div>
    <div id="unique-identifier">
      Start
    </div>
  </div>
</div>

This function searches upwards in the DOM tree until a specific ID is located. It can also be modified to search for specific classes.

Answer №5

Alternative functions can provide different outcomes compared to their counterparts. In this case, the recursive function 'closest' has a unique approach as it searches the children for a specific identifier.

function closestChild(elem) {
    if( elem.className.indexOf("non-unique-identifier") ) {
        return elem;
    } 

    var parent = elem.parentNode;

    for(var i = 0; i< parent.children.length; i++ ) {
        if( parent.children[i].className.indexOf("non-unique-identifier")!=-1)  {
            return parent.children[i];
        }
    }

    return closestChild(parent);
}



var elem = document.getElementById('unique-identifier');

var cl = closestChild(elem);

console.log(cl);

In contrast, here is an alternative example that does not search among the children but rather focuses on finding the closest element based on a given criteria:

function closestParent(elem) {
    if( elem.className.indexOf("non-unique-identifier") ) {
        return elem;
    } 

    var parent = elem.parentNode;

    if( parent.className.indexOf("non-unique-identifier")!=-1) {
        return parent;
    }    

    return closestParent(parent);
}



var elem = document.getElementById('unique-identifier');

var cl = closestParent(elem);

console.log(cl);

Answer №6

<div class="item">
  <div class="itemed">
    <p>Greetings <b class="itemed" id="universe">Universe</b></p>
  </div>
</div>

function findClosestElement(el, classname) {
   if(el.parentNode){
        if(el.parentNode.className.includes(classname)){
        return el.parentNode;
      }
      else{
        return findClosestElement(el.parentNode, classname);
      }
   }
   else{
    return false;
   }
}

var universe = document.getElementById('universe');
var closestElement = findClosestElement(universe, 'item');
console.log(closestElement);

Answer №7

To handle this scenario, utilize JavaScript's closest() function

For example:

var el = document.getElementById('div-03');

var r1 = el.closest("#div-02");  
// retrieves the element with the id=div-02

var r2 = el.closest("div div");  
// finds the nearest ancestor that is a div within a div, which in this case is div-03 itself

var r3 = el.closest("article > div");  
// locates the nearest ancestor that is a div and has an immediate parent article, which here is div-01

var r4 = el.closest(":not(div)");
// identifies the nearest ancestor that is not a div, which is the outermost article
<article>
  <div id="div-01">Here is div-01
    <div id="div-02">Here is div-02
      <div id="div-03">Here is div-03</div>
    </div>
  </div>
</article>

Further details can be found on MDN Web Docs by following this link

Answer №8

I created this straightforward function for one of my TypeScript projects using querySelector on parentNode, allowing you to pass parameters such as class, id, or tag name etc.

findParentNode(el, selector:string):Element | null {
  let found = null;
  let child = el;
  let childSelector = guessSelector(child);

  while(child !== document && found === null) {
    child = child.parentNode;
    childSelector = guessSelector(child);
    found = childSelector ? child.parentNode.querySelector(`${childSelector} > ${selector}`) : null;
  }

  return found;

  function guessSelector(child:any):string {
    childSelector = child.className ? `.${child.className.replace(' ', '.')}` : null;

    if (typeof child.getAttribute === 'function') {
      childSelector = !childSelector ?
        (child.getAttribute('id') ? `#${child.getAttribute('id')}` : null) : childSelector;

      childSelector = !childSelector ?
        child.tagName.toLowerCase() : childSelector;
    }

    return childSelector;
  }
}

Example:

If you're looking to find the closest parent element of target that has the .param-input class, you can achieve it like this:

document.body.addEventListener('click', (e) => {
  console.log(findParentNode(e.target, '.param-input'));
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How to adjust the Timezone of a JavaScript Date without affecting the designated time?

var schedule = { start_at = Date ( '2017-10-10' ), // Data is not editable - ORM will provide ... }; // Mon Oct 09 2017 20:00:00 GMT-0400 (Eastern Daylight Time) console.log ( schedule.start_at ); Seeking a way to adjust the time of an ...

What is the connection between importing and using destructuring in ES6 syntax?

Bring in: import React, { Component } from 'react'; Unpacking: let z, { b } = {a: 1, b: 2, c: 3} Both of these examples seem to have similar syntax. However, in the second example, z will be undefined instead of {a: 1, b: 2, c: 3}. Does this ...

Loading Java Script files in real-time

Is there a method to dynamically load JS files before "$(document).ready" is triggered, while still having them available in the ready event handler? Is there a feature in jQuery that allows for this process? The challenge I am facing involves loading di ...

"Exploring the Power of Angular Change Detection with Promises in a Hybrid

We are currently in the process of upgrading an AngularJS project to Angular 7 by following the recommended "hybrid" approach where both frameworks coexist. However, we have encountered some issues with change detection when dealing with native promises. T ...

Deleting information from several stores in React Reflux using a single action function

In the AuthActions file, there is a straightforward function called _clear that assigns this.data to undefined. This function is only invoked when a user logs out. However, upon logging back in with a different user, remnants of data from the previous ac ...

Lighthouse Issue: Facing PWA Challenges with a "Request Blocked by DevTools" Error

For hours now, I've been struggling to make Lighthouse work in Chrome for my initial PWA project. I feel completely lost as nothing seems to be making sense despite the basic code I have included below. The issue arises when I load the page normally ...

What causes the off-canvas menu to displace my fixed div when it is opened?

Using the Pushy off-canvas menu from GitHub for my website has been great, but it's causing some trouble with my fixed header. When I scroll down the page, the header sticks perfectly to the top, but once I open the off-canvas menu, the header disappe ...

The necessity of ExpressJS

After reviewing the Express.JS 4.x API documentation, I became intrigued by their setup process. Here's my interpretation of how it works: In the provided sample code snippet from the Express.JS 4.x API, the express module is first imported and stored ...

Can a browser still execute AJAX even if the window.location is altered right away?

Here is the situation I am facing: <script> jQuery.ajax{ url : 'some serverside bookkeeping handler', type : post, data : ajaxData }; window.location = 'Some new URL'; </script> Scenario: I n ...

Conflict arises between Angular $scope and the file input type

I have been attempting to convert a file into a byte array using AngularJS. The conversion process is successful and I am able to add the byte code and filename to an array ($scope.FileAttachments). However, there seems to be an issue with ng-repeat not wo ...

What is the best method to make an email address distinct?

<body> <p>Please input your email address:</p> <input id="email" style="margin-bottom: 20px; margin-top: 2px;" type="email" placeholder="Email Address"> <input onclick= "validateEmail(email)" type="su ...

Oops! An error occurred in AngularJs: "TypeError: $scope.todos.push is not a

I am currently facing an issue while using the $http.get method to add a todo from my controller to my database. The error message "TypeError: $scope.todos.push is not a function" keeps appearing, despite trying various solutions suggested by similar quest ...

Creating an object using a string in node.js

I have a string that I am sending from AngularJS to NodeJS in the following format. "{↵obj:{↵one:string,↵two:integer↵}↵}" //request object from browser console To convert this string into an object and access its properties, I am using the serv ...

Angular, PHP, and MySQL working together to establish database connectivity

Greetings! I am facing some challenges with a small project involving mySQL and PHP for the first time. My main focus right now is on establishing connectivity. Despite following various tutorials, I have been unable to connect to the database and keep enc ...

What is the best way to configure my AngularJS routing for managing URL rewriting and page reloads effectively?

As I develop my website using AngularJS, one of my main goals is to create a smooth navigation experience without the annoying "browser flash" effect that occurs when switching between pages. This means that clicking on a link in index.html to go to foo.ht ...

Guide on storing images in a designated folder using CodeIgniter

My code is located in view/admin_view2.php <?php echo form_open_multipart('home_admin/createBerita'); ?> <div class="form-group" > <label class="control-label">upload foto</label> <inpu ...

Guide to automatically inserting text into an html form and submitting it without manual intervention

Currently, I am in the process of a project where my main goal is to design an HTML form for submitting replies. One interesting feature I want to include is an option for users who are feeling lazy to simply click on "auto-generate comment", which will ...

How to Programmatically Disable OnClick Functionality in a Nested Div Using JavaScript and jQuery

I'm currently working on a Javascript application that allows users to enable/disable controls dynamically. While I've had success in disabling/enabling nested inputs and buttons, I'm facing an issue with disabling an onclick event within a ...

Using a JSON string with form field names and corresponding values to automatically fill in form fields using jQuery

My JSON string looks like this: [{"meta_key":"algemeen_reden","meta_value":"oplevering"},{"meta_key":"algemeen_netspanning","meta_value":"230"}] Currently, I am using the following script to fill out form fields: // Grab Algemeen Data get_algemeen_data ...

Is there a way to trigger jQuery animations even when the browser window is not in focus?

Hi there, I am currently working on developing an ajax-based multiplayer game that connects to a server for updates. The game has various game states and requires the GUI to be animated accordingly. However, I have encountered a problem where jquery anima ...