问题 使用Nokogiri将元素添加到元素中


显然是Nokogiri的 add_class 方法只适用于 NodeLists,使此代码无效:

doc.search('a').each do |anchor|
  anchor.inner_text = "hello!"
  anchor.add_class("whatever") # WHOOPS!
end

我该怎么做才能使这段代码有效?我觉得它有点像

doc.search('a').each do |anchor|
  anchor.inner_text = "hello!"
  Nokogiri::XML::NodeSet.new(anchor).add_class("whatever")
end

但这也不起作用。请告诉我,我不必实施自己的 add_class 对于单个节点!


2898
2018-01-30 04:55


起源



答案:


CSS类只是元素的另一个属性:

doc.search('a').each do |anchor|
  anchor.inner_text = "hello!"
  anchor['class']="whatever"
end

由于CSS类在属性中以空格分隔,如果您不确定是否已存在一个或多个类,则需要类似

anchor['class'] ||= ""
anchor['class'] = anchor['class'] << " whatever"

您需要使用显式设置属性 = 而不只是改变为属性返回的字符串。例如,这不会改变DOM:

anchor['class'] ||= ""
anchor['class'] << " whatever"

即使它导致更多的工作,我可能会这样做:

class Nokogiri::XML::Node
  def add_css_class( *classes )
    existing = (self['class'] || "").split(/\s+/)
    self['class'] = existing.concat(classes).uniq.join(" ")
  end
end

如果你不想修补这个类,你可以选择:

module ClassMutator
  def add_css_class( *classes )
    existing = (self['class'] || "").split(/\s+/)
    self['class'] = existing.concat(classes).uniq.join(" ")
  end
end

anchor.extend ClassMutator
anchor.add_css_class "whatever"

编辑:你可以看到,这基本上是Nokogiri内部所做的 add_class 通过单击类查找源找到的方法:

# File lib/nokogiri/xml/node_set.rb, line 136
def add_class name
  each do |el|
    next unless el.respond_to? :get_attribute
    classes = el.get_attribute('class').to_s.split(" ")
    el.set_attribute('class', classes.push(name).uniq.join(" "))
  end
  self
end

13
2018-01-30 05:02



加强ClassMutator!谢谢! - flunder


答案:


CSS类只是元素的另一个属性:

doc.search('a').each do |anchor|
  anchor.inner_text = "hello!"
  anchor['class']="whatever"
end

由于CSS类在属性中以空格分隔,如果您不确定是否已存在一个或多个类,则需要类似

anchor['class'] ||= ""
anchor['class'] = anchor['class'] << " whatever"

您需要使用显式设置属性 = 而不只是改变为属性返回的字符串。例如,这不会改变DOM:

anchor['class'] ||= ""
anchor['class'] << " whatever"

即使它导致更多的工作,我可能会这样做:

class Nokogiri::XML::Node
  def add_css_class( *classes )
    existing = (self['class'] || "").split(/\s+/)
    self['class'] = existing.concat(classes).uniq.join(" ")
  end
end

如果你不想修补这个类,你可以选择:

module ClassMutator
  def add_css_class( *classes )
    existing = (self['class'] || "").split(/\s+/)
    self['class'] = existing.concat(classes).uniq.join(" ")
  end
end

anchor.extend ClassMutator
anchor.add_css_class "whatever"

编辑:你可以看到,这基本上是Nokogiri内部所做的 add_class 通过单击类查找源找到的方法:

# File lib/nokogiri/xml/node_set.rb, line 136
def add_class name
  each do |el|
    next unless el.respond_to? :get_attribute
    classes = el.get_attribute('class').to_s.split(" ")
    el.set_attribute('class', classes.push(name).uniq.join(" "))
  end
  self
end

13
2018-01-30 05:02



加强ClassMutator!谢谢! - flunder


引入nokogiri的 add_class,就像你找到的那样在NodeSet上工作。试图在里面添加类 each 块虽然不起作用,因为那时你正在处理一个单独的节点。

代替:

require 'nokogiri'

html = '<p>one</p><p>two</p>'
doc = Nokogiri::HTML(html)

doc.search('p').tap{ |ns| ns.add_class('boo') }.each do |n|
  puts n.text
end
puts doc.to_html

哪个输出:

# >> one
# >> two
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html><body>
# >> <p class="boo">one</p>
# >> <p class="boo">two</p>
# >> </body></html>

tap 方法,在Ruby 1.9+中实现,允许访问节点列表本身,允许 add_class 将“boo”类添加到的方法 <p> 标签。


1
2018-01-30 06:13



为什么你不能这样做 doc.search('p').add_class('boo').each do ... - Alan H.
您可以使用 doc = Nokogiri::HTML.fragment(html) 如果你不希望Nokogiri添加doctype和其他html和body标签 - Archonic