jQuery: Как выбрать «отсюда до следующей H2»? - PullRequest
30 голосов
/ 14 мая 2009

Я создаю очень простую страницу часто задаваемых вопросов с помощью jQuery. Вот так:

<h2>What happens when you click on this question?</h2>
<p>This answer will appear!</p>

Это все внутри очень специфического div, поэтому я выберу заголовок с $('#faq h2'). Просто, правда? Нажмите на H2, и используйте this.next(), чтобы появился следующий абзац.

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

Так! Проблема:

<h2>What happens when you click on the next question?</h2>
<p>That is an interesting conundrum.</p>
<p>Because the maintainer is kind of long-winded</p>
<p>and many answers will span a few paragraphs.</p>

Итак, как, без добавления в div s, классов и прочего, я могу заставить мою подпрограмму this.next() выбирать все между вопросом, на который был нажат вопрос, и следующим вопросом (заголовок H2)?

Ответы [ 9 ]

27 голосов
/ 07 февраля 2010

Я понимаю, что это старый вопрос, но в jQuery 1.4 теперь есть nextUntil . Так что-то вроде этого должно теперь работать:

$('h2').click(function(){
    $(this).nextUntil('h2').show();
})
21 голосов
/ 14 мая 2009

Интересная проблема. Сначала позвольте мне сказать, что я думаю, что лучшая стратегия - заключить весь ответ в div, а затем решить проблему становится тривиально:

<h2>Question here</h2>
<div>
<p>Answer here</p>
</div>
</h2>Next Question</h2>
...

с:

$(function() {
  $("h2").click(function() {
    $(this).next().toggleClass("highlighted");
  });
});

Но это, как говорится, разрешимо без этого.

$(function() {
  $("h2").click(function() {
    $(this).nextAll().each(function() {
      if (this.tagName == 'H2') {
        return false; // stop execution
      }
      $(this).toggleClass("highlighted");
    });
  });
});

Не очень элегантно, но это будет работать.

Примечание: Это предполагает, что вопросы являются братьями и сестрами. Если их нет, все становится намного сложнее.

7 голосов
/ 14 мая 2009

Не имеет ли смысла использовать CSS-стиль DL list ?

<dl class="faq">
    <dt>Question?</dt>
    <dd>
         <p>Answer</p>
         <p>Answer</p>
         <p>Answer</p>
    </dd>
</dl>

А затем легкий выбор с помощью:

$('+ dd', this); 

это текущий DT выбор.

Или просто оберните каждый ответ в div, так как это имеет смысл и семантически. Однако я думаю, что список DL имеет гораздо больше смысла семантически.

5 голосов
/ 14 мая 2009

Конечно! Просто сделайте цикл while.

$('h2').click(function() {
    var next = $(this).next();
    while (next.length != 0 && next[0].nodeName == 'P')
    {
        next.toggle();
        next = next.next();
    }
});

Это предполагает, что у вас есть только p тегов после h2. Вы можете добавить больше исключений в цикл while, если хотите добавить что-то вроде img.

Или, если вам все равно, что находится между тегами H2, вы можете проверить, что он не равен H2.

$('h2').click(function() {
    var next = $(this).next();
    while (next.length != 0 && next[0].nodeName != 'H2')
    {
        next.toggle();
        next = next.next();
    }
});

Это будет скрывать все после нажатия на Н2 до тех пор, пока не будет найден следующий Н2 или пока вы не подниметесь на уровень в дом.

1 голос
/ 27 октября 2013

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

Вот если пример HTML, для которого работает мое решение:

<h2> text1 </h2>
<p>asd</p>
<p>asd</p>
<p>asd</p>
<p>asd</p>
<h3>kjdf</h3>
<h2>asdk</h2>
<h3>kjdf</h3>
<p>asd</p>
<p>asd</p>
<p>asd</p>
<p>asd</p>
<h2>aqdsa</h2>

Решение:

jQuery( function ( $ ) {
//defining groups for wrapping them in custom divs

    $('h2').each( function () {
        var grpForDiv= [];          
        var next = $(this).next();  
        grpForDiv.push(this);
        while ( next.length!==0 && next!== undefined && next[0].nodeName !== 'H2')
            {
            grpForDiv.push(next[0]);
            next = next.next();
            }       
        jQuery(grpForDiv).wrapAll('<div class="Group1" />');

    } );
} );
1 голос
/ 14 мая 2009

Возможно, это не совсем ответ на ваш вопрос, но вы можете заключить каждый элемент FAQ (то есть каждую пару вопросов / ответов) в элемент DIV. Это будет иметь смысл с точки зрения семантики, и непрограммист, обслуживающий страницу, просто должен будет скопировать полный DIV (без необходимости в классах).

HTML:

<div id="faq">
 <!-- start FAQ item -->
 <div>
  <h2>Question goes here</h2>
  <p>Answer goes here.</p>
  <p>And here.</p>
  <ul>
   <li>Really, use any HTML element you want here.</li>
   <li>It will work.</li>
  </ul>
 </div>
 <!-- end FAQ item -->
 <!-- start FAQ item -->
 <div>
  <h2>Second question goes here</h2>
  <p>Answer to question two.</p>
 </div>
 <!-- end FAQ item -->
</div>

JavaScript (jQuery):

$('#faq div h2').click(function() {
 $(this).parent().find(':not(h2)').show();
});
0 голосов
/ 14 мая 2009

Вы также можете сделать это без редактирования HTML. Это плагин, который я только что написал для этой конкретной цели:

(function($){
    $.fn.nextUntill = function(expr){
        var helpFunction = function($obj, expr){
            var $siblings = $obj.nextAll();
            var end = $siblings.index( $siblings.filter(expr) );
            if(end===-1) return $([]);
            return $siblings.slice(0, end);
        }
        var retObject = new Array();
        this.each(function(){
            $.merge(retObject, helpFunction($(this), expr));
        });
        return $(retObject);
    }
})(jQuery);

Вы можете использовать это так:

 $('h2').click(function(){
    $(this).nextUntill('h2').show(); //this will show all P tags between the clicked h2 and the next h2 assuiming the p and h2 tags are siblings
 });
0 голосов
/ 14 мая 2009

Обратите внимание, что выбран хороший ответ, но я собираюсь сделать что-то подобное, поэтому я работал над этим сверхурочно:)

Я поместил это в пример.

Учитывая HTML:

<h2>Question 1</h2> 
<p>Answer 1</p> 
<p>Answer 1</p> 
<p>Answer 1</p> 
<h2>Question 2</h2> 
<p>Answer 2</p> 
<p>Answer 2</p> 
<p>Answer 2</p> 
<h2>Question 3</h2> 
<p>Answer 3</p> 
<p>Answer 3</p> 
<p>Answer 3</p> 

Сделайте следующее, и вы получите ответ. Я думаю, что сейчас это самое элегантное из всех представленных решений, но я бы очень оценил мнение других по этому поводу.

jQuery.fn.nextUntilMatch = function(selector) { 
  var result = [];   
  for(var n=this.next();(n.length && (n.filter(selector).length == 0));n=n.next())  
      result.push(n[0]);   
  return $(result); 
} 

$(function() { 
  $('h2~p').hide(); 
  $('h2').click(function() { 
    $(this).nextUntilMatch(':not(p)').toggle(); 
  }) 
});
0 голосов
/ 14 мая 2009

Я не думаю, что ваша стратегия решения проблемы правильная, у вас было 6 ответов, которые частично решают вашу проблему .....

Но ваша самая большая проблема в том, что вы говорите, что не можете доверять сопровождающим использовать divs / spans / classes и т. Д.

То, что я думаю, вам следует сделать, это упростить процесс для них и научить их использовать ваш метод, или он НЕ БУДЕТ работать.

добавьте несколько собственных простых разметок и интерпретируйте их.

[Q] What happens when you click this link?[/Q]
[A] This marvellous answer appears [/A]

(jQuery не исправит ошибку 'user')

...