Эквивалент Nokogiri jQuery closest () для поиска первого подходящего предка в дереве - PullRequest
7 голосов
/ 04 ноября 2011

В jQuery есть прекрасный, хотя и несколько ошибочно названный, метод closest () , который перемещается по дереву DOM в поисках подходящего элемента. Например, если у меня есть этот HTML:

<table src="foo">
  <tr>
    <td>Yay</td>
  </tr>
</table>

Предполагая, что element установлено на <td>, тогда я могу вычислить значение src следующим образом:

element.closest('table')['src']

И это будет чисто возвращать "undefined", если отсутствует элемент таблицы или его атрибут src.

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

ancestors = element.ancestors('table')
src = ancestors.any? ? first['src'] : nil

Тройка необходима, потому что сначала возвращает nil, если вызывается для пустого массива. Лучшие идеи?

Ответы [ 3 ]

11 голосов
/ 04 ноября 2011

Вы можете вызвать first для пустого массива, проблема в том, что он вернет nil, и вы не можете сказать nil['src'] без грусти. Вы могли бы сделать это:

src = (element.ancestors('table').first || { })['src']

И если вы находитесь в Rails, вы можете использовать try таким образом:

src = element.ancestors('table').first.try(:fetch, 'src')

Если вы много делаете такого рода вещи, то скрывайте уродство в методе:

def closest_attr_from(e, selector, attr)
  a = e.closest(selector)
  a ? a[attr] : nil
end

, а затем

src = closest_attr_from(element, 'table', 'src')

Вы также можете добавить его прямо в Nokogiri :: XML :: Node (но я бы не рекомендовал его):

class Nokogiri::XML::Node
  def closest(selector)
    ancestors(selector).first
  end
  def closest_attr(selector, attr)
    a = closest(selector)
    a ? a[attr] : nil
  end
end
6 голосов
/ 04 ноября 2011

Вы также можете сделать это с помощью xpath:

element.xpath('./ancestor::table[1]')
3 голосов
/ 04 ноября 2011

Вам нужен атрибут src ближайшего предка таблицы, если он существует? Вместо получения элемента, который может существовать через XPath, а затем, возможно, получения атрибута через Ruby, запросите атрибут непосредственно в XPath:

./ancestor::table[1]/@src

Вы получите либо атрибут, либо ноль:

irb(main):001:0> require 'nokogiri'
#=> true

irb(main):002:0> xml = '<r><a/><table src="foo"><tr><td /></tr></table></r>'
#=> "<r><a/><table src=\"foo\"><tr><td /></tr></table></r>"

irb(main):003:0> doc = Nokogiri.XML(xml)
#=> #<Nokogiri::XML::Document:0x195f66c name="document" children=…

irb(main):004:0> doc.at('td').at_xpath( './ancestor::table[1]/@src' )
#=> #<Nokogiri::XML::Attr:0x195f1bc name="src" value="foo">

irb(main):005:0> doc.at('a').at_xpath( './ancestor::table[1]/@src' )
#=> nil
...