Первый родитель 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();
};