#ruby #dom #nokogiri #closest #ancestor
#ruby #dom #nokogiri #ближайший #предок
Вопрос:
В 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():
ancestors = element.ancestors('table')
src = ancestors.any? ? first['src'] : nil
Троичный необходим, потому что first возвращает nil, если вызывается для пустого массива. Лучшие идеи?
Ответ №1:
Вы можете вызвать 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
Комментарии:
1. Ответ принят для полной исчерпываемости — возможно, просто отправьте свой патч в Nokogiri в качестве запроса на извлечение!
2. Ruby 2.3 вводит оператор безопасной навигации ,
amp;.
, поэтому вы можете сделатьelement.ancestors('table').firstamp;.src
Ответ №2:
Вы также можете сделать это с помощью xpath:
element.xpath('./ancestor::table[1]')
Ответ №3:
Вам нужен src
атрибут ближайшего предка таблицы, если он существует? Вместо того, чтобы получать элемент, который может существовать через XPath, а затем, возможно, получать атрибут через Ruby, запрашивайте атрибут непосредственно в XPath:
./ancestor::table[1]/@src
Вы получите либо атрибут, либо nil:
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