DOM / pure JavaScript solution to jQuery.closest() implementation?

后端 未结 8 1849
感动是毒
感动是毒 2020-12-06 00:26

Here\'s the markup i\'m trying to query. So given the markup:

相关标签:
8条回答
  • 2020-12-06 00:51

    I wrote this simple function in one of my TypeScript projects so i used querySelector on parentNode so you can pass to function class, id, 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 want to find closest parent of target which has .param-input class you can do this like this:

    document.body.addEventListener('click', (e) => {
      console.log(findParentNode(e.target, '.param-input'));
    });
    
    0 讨论(0)
  • 2020-12-06 00:58

    Alternative is a recursive function. This is slightly different to closest as i searches the children, I'm not sure if closest does.

    function closest(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 closest(parent);
    }
    
    
    
    var elem = document.getElementById('unique-identifier');
    
    var cl = closest(elem);
    
    console.log(cl);
    

    Non children searching example (more like closest):

    function closest(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 closest(parent);
    }
    
    
    
    var elem = document.getElementById('unique-identifier');
    
    var cl = closest(elem);
    
    console.log(cl);
    
    0 讨论(0)
  • 2020-12-06 00:58
    <div class="item">
      <div class="itemed">
        <p>Hello <b class="itemed" id="world">World</b></p>
      </div>
    </div>
    
    function closest(el, classname) {
       if(el.parentNode){
            if(el.parentNode.className.includes(classname)){
            return el.parentNode;
          }
          else{
            return closest(el.parentNode, classname);
          }
       }
       else{
        return false;
       }
    }
    
    var world = document.getElementById('world');
    var closest = closest(world, 'item');
    console.log(closest);
    
    0 讨论(0)
  • 2020-12-06 01:01

    Concise and quick (tested with Benchmark.js) way to search for closest element by any css selector:

    var ep = Element.prototype;
    ep.matches = ep.matches || ep.webkitMatchesSelector || ep.msMatchesSelector || ep.mozMatchesSelector;
    
    function getClosest( elem, selector ) {
        while (elem !== document.body) {
            elem = elem.parentElement;
            if (elem.matches(selector)) return elem;
        }
    }
    

    Supports IE9+ and the rest of the browsers you can expect to care about.

    0 讨论(0)
  • 2020-12-06 01:02

    You can't do this without a loop :

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

    Well, actually you can, using recursivity (a disguised loop) :

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

    A demo (using Sizzle for the 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",
      "#looser" , "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>

    0 讨论(0)
  • 2020-12-06 01:04

    To add an updated answer, there is now Element.closest(<query_selector>) available.

    https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

    This isn't supported on IE, but that mozilla doc page includes code for a polyfill for IE8 and IE9+.

    0 讨论(0)
提交回复
热议问题