Nokogiri equivalent of jQuery closest() method for finding first matching ancestor in tree

末鹿安然 提交于 2019-12-05 05:34:58

You can call first on an empty array, the problem is that it will return nil and you can't say nil['src'] without getting sad. You could do this:

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

And if you're in Rails, you could use try thusly:

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

If you're doing this sort of thing a lot then hide the ugliness in a method:

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

and then

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

You could also patch it right into Nokogiri::XML::Node (but I wouldn't recommend it):

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

You can also do this with xpath:

element.xpath('./ancestor::table[1]')

You want the src attribute of the closest table ancestor, if it exists? Instead of getting an element that might exist via XPath and then maybe getting the attribute via Ruby, ask for the attribute directly in XPath:

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

You'll get either the attribute or 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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!