Первый родитель jQuery, содержащий всех детей

Как мне получить выбор первого родителя из набора элементов, который содержит ВСЕ из этих элементов?

Например:

<body>
 <dl>
  <dt>Items:</dt>
  <dd>
   <ul>
    <li>Item 1<div class="item-info">...</div></li>
    <li>Item 2<div class="item-info">...</div></li>
    <li>Item 3<div class="item-info">...</div></li>
   </ul>
  </dd>
 </dl>
</body>

Мне нужно что-то вроде этого:

$('.item-info').commonParent();

и он вернет эквивалент:

[$('ul')]

Есть ли простой способ сделать это с помощью селекторов jQuery? Или мне придется написать плагин?

Ответы

Ответ 1

Если вы действительно ищете низшего общего предка (Посмотрите, как это работает в скрипке):

jQuery.fn.commonAncestor = function() {
  var parents = [];
  var minlen = Infinity;

  $(this).each(function() {
    var curparents = $(this).parents();
    parents.push(curparents);
    minlen = Math.min(minlen, curparents.length);
  });

  for (var i in parents) {
    parents[i] = parents[i].slice(parents[i].length - minlen);
  }

  // Iterate until equality is found
  for (var i = 0; i < parents[0].length; i++) {
    var equal = true;
    for (var j in parents) {
      if (parents[j][i] != parents[0][i]) {
        equal = false;
        break;
      }
    }
    if (equal) return $(parents[0][i]);
  }
  return $([]);
}

Пример

<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="jquery.commonancestor.js"></script>


$(function() {
  console.log($(".item-info").commonAncestor());
});
</script>
<body>
 <dl>
  <dt>Items:</dt>
  <dd>
   <ul>
    <li>Item 1<b><div class="item-info">...</div></b></li>
    <li>Item 2<u><div class="item-info">...</div></u></li>
    <li>Item 3<i><div class="item-info">...</div></i></li>
   </ul>
  </dd>
 </dl>
</body>

Это не было проверено строго, поэтому, пожалуйста, укажите любые ошибки, которые вы видите.

EDIT Возвращал родительский вместо $(родительский)

EDIT Не работал в IE8

Ответ 2

Я предполагаю, что элементы .item-info (потенциально) разбросаны по всей странице.

Если это правильно, попробуйте следующее: http://jsfiddle.net/EJWjf/1/

var $items = $('.item-info');   // Cache all the items in question
var total = $items.length;      // Cache the total number
var parent;                     // Will store the match

$items.first()           // Grab the first item (an arbitrary starting point)
    .parents()           // Get all of its parent elements
    .each(function() {
            // Iterate over each parent, finding the .info-item elements
            //    it contains, and see if the quantity matches the total
        if($(this).find('.item-info').length == total) {
            parent = this;  // If so, we found the closest common ancestor so
            return false;   //     store it and break out of the loop
        }
    });

alert(parent.tagName);

Здесь версия функции: http://jsfiddle.net/EJWjf/2/

function findCommon(selector) {
    var $items = $(selector);   
    var total = $items.length;      
    var parent;                   

    $items.first()
        .parents()
        .each(function() {
            if($(this).find(selector).length == total) {
                parent = this;
                return false;
            }
        });

    return $(parent);
}

var result = findCommon('.item-info');

Ответ 3

Вот моя версия. Он выполняет лучшие всех ответов типа jQuery-plugin на странице. Это единственный, который обрабатывает пустой объект jQuery $() без ошибок. Это и ответ Джейми Вонга являются единственными, которые обрабатывают объект jQuery с одним элементом без ошибок.

Я построил jsfiddle, демонстрирующий все выпуски версий. Вы можете щелкнуть кнопки Preset, чтобы легко увидеть различные тестовые примеры, или введите свой собственный селектор и нажмите "Найти", чтобы увидеть результат.

jQuery.fn.reverse = function() {
   return Array.prototype.reverse.call(this);
};

jQuery.fn.commonAncestor = function() {
   var i, l, current,
      compare = this.eq(0).parents().reverse(),
      pos = compare.length - 1;
   for (i = 1, l = this.length; i < l && pos > 0; i += 1) {
      current = this.eq(i).parents().reverse();
      pos = Math.min(pos, current.length - 1);
      while (compare[pos] !== current[pos]) {
         pos -= 1;
      }
   }
   return compare.eq(pos);
};

В случае, когда один из элементов является предком другого, все ответы, данные до сих пор, возвращают родительский элемент предка. Обе вышеперечисленные ссылки включают версию моей функции, если, например, на странице есть div, $('body, div').commonAncestor() вернет body вместо html

Примечание. Эта функция предполагает, что все элементы в наборе jQuery находятся в текущем документе. Для получения элементов из двух разных документов в один и тот же объект jQuery потребовалось бы особое и странное усилие - и в этом случае моя функция не удастся (так же как и все остальные на странице). Это можно было бы исправить, начиная с изменения условия pos > 0 на pos >= 0, заставив обработчик while в течение 1 и, возможно, изменив return compare.eq(pos) на return $(compare[pos]) (так как функция eq() использует отрицательные индексы для индексации назад от конца, а не пустым).

Ответ 4

Это значительно быстрее, чем все остальные (включая ErikE), особенно для множества элементов:

jQuery.fn.commonAncestor = function() {
  if (!this.length)
    return null;

  var parent = this[0].parentNode;
  for (var i = this.length - 1; i >= 1; --i) {
    var elt = this[i];
    if (elt.contains(parent))
      parent = elt.parentNode;
    else
      while (!parent.contains(elt))
        parent = parent.parentNode;
  }
  return parent;
};

Ответ 5

Вы ищете любой из них?

$("element").parents() // return all parents until reaches 'html'

$("element").parent() // return the first parent

$("element").closest("parent") // return the closest selected parent

Ответ 6

Я считаю, что это то, что вы ищете:

JQuery

Первые четыре строки содержат код, который делает фактический захват UL. Остальное просто для отображения содержимого совпадающих UL.

<script type="text/javascript">
  $(document).ready(function() {
    // The code that grabs the matching ULs
    var ULsWithAll=$("ul").has(".item-info").filter( function(index) { // Grab all ULs that has at least one .item-info
      var ChildrenOfUL=$(this).children("li");  // Grab the children

      return ChildrenOfUL.length===ChildrenOfUL.has(".item-info").length;  // Are all of them .item-info
    });

    $(ULsWithAll).each(function(){ // Output the ULs that were found
      alert($(this).html());
    })
  });
</script>

HTML

<dl>
  <dt>Items 1:</dt>
  <dd>
    <ul>
      <li>Item 1-1<div class="item-info">...</div></li>
      <li>Item 1-2<div class="item-info">...</div></li>
      <li>Item 1-3<div class="item-info">...</div></li>
    </ul>
  </dd>
  <dt>Items 2:</dt>
  <dd>
    <ul>
      <li>Item 2-1<div class="item-info">...</div></li>
      <li>Item 2-2<div class="item-info">...</div></li>
      <li>Item 2-3<div class="item-info">...</div></li>
    </ul>
  </dd>
  <dt>Items 3:</dt>
  <dd>
    <ul>
      <li>Item 3-1<div class="item-info">...</div></li>
      <li>Item 3-2<div class="item-info">...</div></li>
      <li>Item 3-3<div>...</div></li>
    </ul>
  </dd>
</dl>

Вышеупомянутый код jQuery вернет UL для первого и второго ударов (третий - dud). Проверьте его на http://jsfiddle.net/ksTtF/

Другой пример (без предупреждения): http://jsfiddle.net/vQxGC/

Ответ 7

Вдохновленный из ряда других ответов на quiestion, я создал версию, которая, как я нашел, соответствовала моим требованиям. http://jsfiddle.net/qDRv5/ (обратите внимание, что он записывает найденный родительский элемент в консоль)

jQuery.fn.firstParent=function (f){
    //Set the variables for use;
    s=$(this);
    var i,j;
    var p=[];
    var pv=true;

    //make sure there is something to itterate over
    if(s.length>0){

        //build a multidimentional array of parents
        s.each(function(){p.push($(this).parents());});

        //itterate through the parents of the first element starting at the closest
        for(i=0;i<p[0].length;i++){

            //itterate through all elements after the first
            for(j=1;j<s.length;j++){

                //look for the current parent of the first element in each array
                pv=($.inArray(p[0][i],p[j]) != -1);

                //if we are looking at the last element and it matchs, return that parent
                if(pv==true && j==s.length-1) return typeof(f)=='string'?$(p[0][i]).find(f):$(p[0][i]);
            }
        }
    }
    return false;
}

Начну с поиска элемента, который наименее продвигает путь от корня, потому что я точно знаю, что этот элемент не будет иметь общего предка дальше от корня. Затем я отступаю от родителя за раз, из этих элементов проверяет, имеют ли эти родительские элементы все остальные элементы в качестве дочерних элементов. Когда я нахожу родительские элементы, у которых есть все элементы как дети, моя миссия завершена.

Ответ 8

Вы забыли?... Это просто Javascript...

$('.item-info').get(0).parentElement

Ответ 9

так что... я знаю, что это старый поток, но мне нужна была эта точная функциональность, и мне нужно было работать в красивой цепочке, которая является jQuery;), вот что я придумал:

jQuery.fn.firstParent=function (f){
    //Set the variables for use;
    s=$(this);
    var i,j;
    var p=[];
    var pv=true;

    //make sure there is something to itterate over
    if(s.length>0){

        //build a multidimentional array of parents
        s.each(function(){p.push($(this).parents());});

        //itterate through the parents of the first element starting at the closest
        for(i=0;i<p[0].length;i++){

            //itterate through all elements after the first
            for(j=1;j<s.length;j++){

                //look for the current parent of the first element in each array
                pv=($.inArray(p[0][i],p[j]) != -1);

                //if we are looking at the last element and it matchs, return that parent
                if(pv==true && j==s.length-1) return typeof(f)=='string'?$(p[0][i]).find(f):$(p[0][i]);
            }
        }
    }
    return false;
}

есть также jsfiddle здесь

Ответ 10

Я бы сделал что-то вроде этого: отфильтруйте родительские селектора, которые содержат каждый элемент в селекторном наборе.

jQuery.fn.commonParent = function() {
  var parent = this.parents();
  this.each(function() {
    parent = parent.has(this);
  });
  return parent.first();
};