JQuery найти следующие/предыдущие элементы определенного класса, но не обязательно братьев и сестер
Следующие, prev, nextAll и prevAll методы очень полезны, но не если элементы, которые вы пытаетесь найти, не находятся в одном и том же родительском элементе. Я хочу сделать что-то вроде этого:
<div>
<span id="click">Hello</span>
</div>
<div>
<p class="find">World></p>
</div>
Когда нажата область с id click
, я хочу сопоставить следующий элемент с классом find
, который в этом случае не является братом элемента с щелчком, поэтому next()
или nextAll()
не будет работать.
Ответы
Ответ 1
Я сам работал над этой проблемой сегодня, вот что я придумал:
/**
* Find the next element matching a certain selector. Differs from next() in
* that it searches outside the current element parent.
*
* @param selector The selector to search for
* @param steps (optional) The number of steps to search, the default is 1
* @param scope (optional) The scope to search in, the default is document wide
*/
$.fn.findNext = function(selector, steps, scope)
{
// Steps given? Then parse to int
if (steps)
{
steps = Math.floor(steps);
}
else if (steps === 0)
{
// Stupid case :)
return this;
}
else
{
// Else, try the easy way
var next = this.next(selector);
if (next.length)
return next;
// Easy way failed, try the hard way :)
steps = 1;
}
// Set scope to document or user-defined
scope = (scope) ? $(scope) : $(document);
// Find kids that match selector: used as exclusion filter
var kids = this.find(selector);
// Find in parent(s)
hay = $(this);
while(hay[0] != scope[0])
{
// Move up one level
hay = hay.parent();
// Select all kids of parent
// - excluding kids of current element (next != inside),
// - add current element (will be added in document order)
var rs = hay.find(selector).not(kids).add($(this));
// Move the desired number of steps
var id = rs.index(this) + steps;
// Result found? then return
if (id > -1 && id < rs.length)
return $(rs[id]);
}
// Return empty result
return $([]);
}
Итак, в вашем примере
<div><span id="click">hello</span></div>
<div><p class="find">world></p></div>
теперь вы можете найти и управлять элементом 'p', используя
$('#click').findNext('.find').html('testing 123');
Я сомневаюсь, что он будет хорошо работать на больших структурах, но вот он:)
Ответ 2
Попробуйте это. Он будет отмечать ваш элемент, создать набор элементов, соответствующих вашему селектору, и собрать все элементы из набора, следующего за вашим элементом.
$.fn.findNext = function ( selector ) {
var set = $( [] ), found = false;
$( this ).attr( "findNext" , "true" );
$( selector ).each( function( i , element ) {
element = $( element );
if ( found == true ) set = set.add( element )
if ( element.attr("findNext") == "true" ) found = true;
})
$( this ).removeAttr( "findNext" )
return set
}
ИЗМЕНИТЬ
гораздо проще, используя метод индекса jquerys. элемент, который вы вызываете из этого метода, должен быть выбран одним и тем же селектором, хотя
$.fn.findNext = function( selector ){
var set = $( selector );
return set.eq( set.index( this, ) + 1 )
}
чтобы освободить функцию из этого гандикапа, мы могли бы использовать собственные браузеры compareDocumentposition
$.fn.findNext = function ( selector ) {
// if the stack is empty, return the first found element
if ( this.length < 1 ) return $(s).first();
var found,
that = this.get(0);
$( selector )
.each( function () {
var pos = that.compareDocumentPosition( this );
if ( pos === 4 || pos === 12 || pos === 20 ){
// pos === 2 || 10 || 18 for previous elements
found = element;
return false;
}
})
// using pushStack, one can now go back to the previous elements like this
// $("#someid").findNext("div").remove().end().attr("id")
// will now return "someid"
return this.pushStack( [ found ] );
},
ИЗМЕНИТЬ 2
это намного проще с помощью jQuery $.grep. здесь новый код
$.fn.findNextAll = function( selector ){
var that = this[ 0 ],
selection = $( selector ).get();
return this.pushStack(
// if there are no elements in the original selection return everything
!that && selection ||
$.grep( selection, function( n ){
return [4,12,20].indexOf( that.compareDocumentPosition( n ) ) > -1
// if you are looking for previous elements it should be [2,10,18]
})
);
}
$.fn.findNext = function( selector ){
return this.pushStack( this.findNextAll( selector ).first() );
}
при сжатии имен переменных это становится всего лишь двумя слоями.
Изменить 3
используя побитовые операции, эта функция может быть еще быстрее?
$.fn.findNextAll = function( selector ){
var that = this[ 0 ],
selection = $( selector ).get();
return this.pushStack(
!that && selection || $.grep( selection, function(n){
return that.compareDocumentPosition(n) & (1<<2);
// if you are looking for previous elements it should be & (1<<1);
})
);
}
$.fn.findNext = function( selector ){
return this.pushStack( this.findNextAll( selector ).first() );
}
Ответ 3
Мое решение потребует некоторой корректировки вашей разметки, чтобы сделать jQuery намного проще. Если это невозможно или не привлекательный ответ, пожалуйста, игнорируйте!
Я бы обернул "родительскую" оболочку вокруг того, что вы хотите сделать...
<div class="find-wrapper">
<div><span id="click">hello</span></div>
<div><p class="find">world></p></div>
</div>
Теперь, чтобы найти find
:
$(function() {
$('#click').click(function() {
var $target = $(this).closest('.find-wrapper').find('.find');
// do something with $target...
});
});
Это дает вам гибкость в том, чтобы иметь какую-либо разметку и иерархию, которые вы хотели бы использовать в предлагаемой оболочке, и по-прежнему надежно находить свою цель.
Удачи!
Ответ 4
Я думаю, что единственный способ решить эту проблему - сделать рекурсивный поиск по элементам после текущего элемента. Нет простого решения этой проблемы, предоставляемой jQuery. Если вы хотите только найти элементы в родстве вашего родительского элемента (как в случае вашего примера), не требуется выполнять рекурсивный поиск, но вам нужно выполнить несколько поисков.
Я создал пример (на самом деле, он не рекурсивный), который делает то, что вы хотите (я надеюсь). Он выбирает все элементы после текущего элемента clicked и делает их красными:
<script type="text/javascript" charset="utf-8">
$(function () {
$('#click').click(function() {
var parent = $(this);
alert(parent);
do {
$(parent).find('.find').css('background-color','red');
parent = $(parent).parent();
} while(parent !== false);
});
});
</script>
Ответ 5
Следующее выражение должно (запретить синтаксические ошибки!) найти всех братьев и сестер родителя, которые содержат элемент p.find
, а затем найти те элементы p.find
и изменить их цвет на синий.
$(this).parent().nextAll(":has(p.find)").find(".find").css('background-color','blue');
Конечно, если ваша структура страницы такова, что p.find
происходит на совершенно ином уровне иерархии (например, родной бабушка или дедушка), это не сработает.