Nokogiri equivalent of jQuery closeest () method for finding the first matching ancestor in a tree

jQuery has a beautiful, but somewhat incorrectly named closest () method that raises the DOM tree looking for a suitable element. For example, if I have this HTML:

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

Assuming element set to <td> , then I can determine the src value as follows:

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

And this will purely return "undefined" if the table element or its src attribute is missing.

Being used to it in Javascriptland, I would like to find something equivalent for Nokogiri in Rubyland, but the closest I could come up with is obviously an inelegant hack using ancestors () :

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

Ternar is necessary because it returns nil first if called in an empty array. Best ideas?

+6
source share
3 answers

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

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

And if you are in Rails, you can use try as follows:

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

If you do so much, then hide the ugliness in the 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 can also install it directly in Nokogiri :: XML :: Node (but I would not 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 
+9
source

You can also do this with xpath:

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

Do you need the src attribute of the closest ancestor of the table, if one exists? Instead of getting an element that can exist through XPath, and then possibly getting an attribute through Ruby, ask for the attribute directly in XPath:

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

You will get either an attribute or zero:

 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 
+3
source

Source: https://habr.com/ru/post/900741/


All Articles