Выбрать дочерний элемент jQuery

У меня есть таблица, структурированная следующим образом:

<table>
  <tbody>
  for loop here
    <tr>
      <td>Item X Attribute 1</td>
      <td>Item X Attribute 2</td>
      <td><button name="test" onclick="display_variants()">Hide my kids</button></td>
    </tr>
    <tr>
      <tbody class="variants_info">
        <tr>
          <td>Item X Variant 1</td>
          <td>Item X Variant 2</td>
        </tr>
      </tbody>
    </tr>
endfor loop
  </tbody>
</table>

Таким образом, структура повторяет Y раз.

Я пытаюсь создать script, который скрывает tbody.variants выбранной строки.

Что я до сих пор знаю:

<script>
  function display_variant(){
    if($('.variants_info').is(":visible")){
        $('.variants_info').hide();
    }
    else{
        $('.variants_info').show();
    }
  }
</script>

но это скрывает варианты из ВСЕ строк.

Есть ли способ выбрать дочерние элементы определенной строки? Также как я могу начать с tbody.variants скрытым? (Начать, как при посещении страницы).

Изменить: На данный момент это пристально смотрит на это: введите описание изображения здесь

Update: Мне удалось изменить структуру:

<table>
  for loop
    <tbody>
      <tr>
        <td>image for el 1</td>
        <td>attr for el 1</td>
        <td><button type='button' name='test'>Click Me</button></td>
      </tr>
      for loop
        <tr class='variants_info'>
          <td>variant1 for el 1</td>
          <td>variant2 for el 1</td>
        </tr>
      endfor
    </tbody>
  endfor
</table>

Обновление 2: Фактический код:

<table class="pure-table pure-table-bordered">
  <tbody>
  {% for product in collection.products %}
  {% if product.available %}

    <tr class="{% cycle 'pure-table-odd', '' %}">
      <td>
        <img src="{{ product.featured_image.src | product_img_url: 'thumb' }}" alt="{{ product.featured_image.alt | escape }}" />
      </td>
      <td>{{ product.title }}</td>
      <td style="text-align:right">
        <button type="button" name="click_me">Click Me</button>
      </td>
      </tr>
      {% for variant in product.variants %}
    <tr>
      <td>{{variant.title}}</td>
      <td>{{variant.price | money }}</td>
    </tr>
    {% endfor %}       
        {% endif %}
        {% endfor %}
    </tbody>
</table>

<script>
  $("td button").click(function(){
    $(this).closest('tr').next().toggle();
  })
</script>

Я все еще не могу заставить JS работать. =/Текущий одно только скрывает первый вариант (вместо всех вариантов для нажатой кнопки)

Ответ 1

Я бы предложил пометить строки, содержащие продукты с именем класса, например. "продукт", например:

<tr class="product {% cycle 'pure-table-odd', '' %}">
  <td>
    <img src="{{ product.featured_image.src | product_img_url: 'thumb' }}" 
         alt="{{ product.featured_image.alt | escape }}" />
  </td>
  <td>{{ product.title }}</td>
  <td style="text-align:right">
    <button type="button" name="click_me">Click Me</button>
  </td>
</tr>

Вы не добавили бы этот класс к строкам, у которых есть варианты.

Затем в вашем JavaScript используйте метод nextUntil для соответствия всем следующим рядам вариантов (которые не имеют класса "product" ) до тех пор, пока, но исключая следующую строку продукта и применяя метод toggle() ко всем этим:

$("td button").click(function(){
    $(this).closest('tr').nextUntil('.product').toggle();
});

Альтернативная структура 1

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

<table class="pure-table pure-table-bordered">
  <tbody>
  {% for product in collection.products %}
  {% if product.available %}

    <tr class="{% cycle 'pure-table-odd', '' %}">
      <td>
        <img src="{{ product.featured_image.src | product_img_url: 'thumb' }}" 
             alt="{{ product.featured_image.alt | escape }}" />
      </td>
      <td>{{ product.title }}</td>
      <td style="text-align:right">
        <button type="button" name="click_me">Click Me</button>
      </td>
    </tr>
    <tr>
        <table class="some other class specific for variants">
          <tbody> 
            {% for variant in product.variants %}
            <tr>
              <td>{{variant.title}}</td>
              <td>{{variant.price | money }}</td>
            </tr>
            {% endfor %}
          </tbody>
        </table>
    </tr>
  {% endif %}
  {% endfor %}
  </tbody>
</table>

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

Код JavaScript тогда должен был бы быть таким, каким он был изначально:

$("td button").click(function(){
    $(this).closest('tr').next().toggle();
});

Альтернативная структура 2

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

Обратите внимание, что в отличие от элементов <thead>, <tfoot> и <caption>, однако допускается несколько элементов <tbody> (если они последовательны), позволяя разделять строки данных в длинных таблицах на разные разделы, каждый отдельно отформатированный по мере необходимости.

Это отличается от вашего исходного HTML, где у вас были tbody элементы как дочерние элементы tr, что является недопустимым HTML.

Это будет выглядеть так:

<table class="pure-table pure-table-bordered">
  {% for product in collection.products %}
  {% if product.available %}

  <tbody>
    <tr class="{% cycle 'pure-table-odd', '' %}">
      <td>
        <img src="{{ product.featured_image.src | product_img_url: 'thumb' }}" 
             alt="{{ product.featured_image.alt | escape }}" />
      </td>
      <td>{{ product.title }}</td>
      <td style="text-align:right">
        <button type="button" name="click_me">Click Me</button>
      </td>
    </tr>
    {% for variant in product.variants %}
    <tr>
      <td>{{variant.title}}</td>
      <td>{{variant.price | money }}</td>
    </tr>
    {% endfor %}
  </tbody>
  {% endif %}
  {% endfor %}
</table>

Затем вы можете использовать метод nextAll(), чтобы скрыть строки вариантов, поскольку они ограничены оболочкой tbody из следующей строки продукта:

$("td button").click(function(){
    $(this).closest('tr').nextAll().toggle();
});

Первоначально скрытие строк варианта

Если вы хотите, чтобы все варианты были сначала спрятаны, добавьте следующий атрибут к этим строкам в HTML-коде:

<tr style="display:hidden">

Вы бы, конечно, не делали этого для строк продукта. Кроме того, вы можете определить для этого класс CSS (например, tr.hidden { display:hidden; } вместо style).

Метод jQuery toggle() будет переопределять этот style при отображении и снова восстановить его при скрытии.

Ответ 2

Ваш HTML недействителен, поэтому вы можете его изменить, а затем используйте DEMO

$("td button").click(function() {
  $(this).closest('tr').next().toggle();
})

Ответ 3

попробуйте следующее: -

$("td button").click(function() {
  $(this).closest('tr').nextUntil("tbody").toggle();
});

Демо-ссылка jsfiddle: - https://jsfiddle.net/Lg0wyt9u/776/