2009-12-01 16 views
16

Los métodos next, prev, nextAll y prevAll son muy útiles, pero no si los elementos que intentas encontrar no están en el mismo elemento primario. Lo que quiero hacer es algo como esto:jQuery encuentra elementos next/prev de cierta clase pero no necesariamente hermanos

<div> 
    <span id="click">Hello</span> 
</div> 
<div> 
    <p class="find">World></p> 
</div> 

Cuando se pulsa el lapso con el id click, quiero coincidir con el siguiente elemento con la clase find, que en este caso no es un hermano del hecho clic elemento así next() o nextAll() no funcionará.

+0

posible duplicado de [jQuery para encontrar todos los elementos anteriores que coincidan con una expresión] (http://stackoverflow.com/questions/322912/jquery-to-find-all-previous-elements-that-match- an-expression) – sachleen

Respuesta

8

yo estaba trabajando en este problema a mí mismo hoy en día, esto es lo que ocurrió:

/** 
* Find the next element matching a certain selector. Differs from next() in 
* that it searches outside the current element's 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 $([]); 
} 

Así que en su ejemplo

<div><span id="click">hello</span></div> 
<div><p class="find">world></p></div> 

ahora se podían encontrar y manipular el elemento 'p' usando

$('#click').findNext('.find').html('testing 123'); 

dudo que tenga un buen rendimiento en grandes estructuras, pero aquí está :)

+0

No funciona si el elemento que desea seleccionar es un hermano. Y declara la variable global "heno". –

+1

¿Y quién modificaría esto para encontrar Anterior? – Relm

+0

muy bien escrito! –

0

Creo que la única manera de resolver este problema es hacer una búsqueda recursiva sobre los elementos después del elemento actual. No hay una solución simple a este problema proporcionada por jQuery. Si solo desea encontrar elementos en los hermanos de su elemento primario (como es el caso en su ejemplo), no es necesario realizar una búsqueda recursiva, pero debe realizar búsquedas múltiples.

He creado un ejemplo (en realidad, no es recursivo) que hace lo que quiere (espero). Se selecciona todos los elementos después del elemento hecho clic actual y los convierte en rojo:

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

La siguiente expresión debe (salvo errores de sintaxis!) Buscar todos los hermanos del padre que contienen un elemento p.find y luego encontrar esos p.find elementos y el cambio su color a azul.

$(this).parent().nextAll(":has(p.find)").find(".find").css('background-color','blue'); 

Por supuesto, si su estructura es tal que la página p.find se produce en un nivel totalmente diferente de la jerarquía (hermano de un abuelo, por ejemplo), no va a funcionar.

3

Mi solución implicaría ajustar un poco su marcado para hacer el jQuery mucho más fácil. Si esto no es posible o no es una respuesta atractiva, ¡por favor ignórenlo!

Me envolver un envoltorio 'padre' en torno a lo que quieres hacer ...

<div class="find-wrapper"> 
    <div><span id="click">hello</span></div> 
    <div><p class="find">world></p></div> 
</div> 

Ahora, para encontrar el find:

$(function() { 
    $('#click').click(function() { 
     var $target = $(this).closest('.find-wrapper').find('.find'); 
     // do something with $target... 
    }); 
}); 

Esto le da la flexibilidad necesaria para tener lo tipo de marcado y jerarquía que le gustaría dentro del contenedor que sugerí, y aún así encontrar su objetivo de manera confiable.

¡Buena suerte!

13

Pruebe esto. Marcará su elemento, creará un conjunto de elementos que coincidan con su selector y recogerá todos los elementos del conjunto que sigue a su elemento.

$.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 
} 

EDITAR

mucho más simple solución utilizando jquerys método de índice. el elemento se llama al método de las necesidades para ser seleccionable por el mismo selector aunque

$.fn.findNext = function(selector){ 
    var set = $(selector); 
    return set.eq(set.index(this,) + 1) 
} 

para liberar la función de esta desventaja, podríamos youse los navegadores poseen 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 ]); 
}, 

EDIT 2 esto es mucho más fácil usando $ .grep de jQuery. aquí está el nuevo código

$.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()); 
    } 

al comprimir los nombres de las variables, esto se convierte en un simple dos delineador.

Editar 3 usando operaciones bit a bit, ¿esta función puede ser aún más rápida?

$.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()); 
} 
+0

Una desventaja que no he pensado es que el elemento al que llama findNext, debe ser seleccionable por el mismo selector, sino simplemente recorrerá todo sin encontrar el elemento marcado. – lordvlad

+0

Gracias, he mirado por todas partes y nadie más ha respondido el caso de los no hermanos tan sucintamente como usted. –

+0

Gracias por su trabajo. ¡Con documentos tabulares muy grandes, Edit 2 aceleró lo que estaba tratando de hacer por órdenes de magnitud! – David

Cuestiones relacionadas