Подтвердить что ты не робот

DOM/чистое решение JavaScript для реализации jQuery.closest()?

Здесь разметка, которую я пытаюсь выполнить. Поэтому, учитывая разметку:

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

Я запрашиваю # уникального идентификатора:

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

Затем я пытаюсь выбрать таблицу. Проблема в том, что я хочу сделать код не хрупким, поэтому мне не нужно это делать:

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

Мой вопрос

Есть ли в настоящее время реализация DOM эквивалента jQuery для $(). closeest()? Было бы предпочтительной ближайшая реализация, эффективная без вложенных циклов.

Ограничения

Мне нужно не использовать jQuery или sizzle для этой конкретной проблемы или вводить какие-либо новые библиотеки. Код также довольно старый. Таким образом, это является причиной таких ограничений и существования <tables>.

4b9b3361

Ответ 1

Вы не можете сделать это без цикла:

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

Ну, на самом деле вы можете, используя рекурсию (замаскированный цикл):

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

Демонстрация (используя Sizzle для запросов DOM):

// 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>

Ответ 2

function closestById(el, id) {
  while (el.id != id) {
    el = el.parentNode;
    if (!el) {
      return null;
    }
  }
  return el;
}

// Use it like:

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

Ответ 3

Альтернатива - это рекурсивная функция. Это немного отличается от ближайшего, так как я ищу детей, я не уверен, что ближе к ним.

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);

Пример поиска для детей (более близкий к ближайшему):

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);

Ответ 4

Краткий и быстрый (проверенный с помощью Benchmark.js) способ поиска ближайшего элемента любым селектором css:

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;
    }
}

Поддерживает IE9 + и остальные браузеры, о которых вы можете ожидать.

Ответ 5

<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 == 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);

Ответ 6

Я написал эту простую функцию в одном из моих проектов TypeScript, поэтому я использовал querySelector на parentNode, чтобы вы могли перейти к функции class, id, tag name и т.д.

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;
  }
}

Пример:

Если вы хотите найти ближайшего родителя target, который имеет класс .param-input, вы можете сделать это следующим образом:

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