Добавить класс к элементу с Nokogiri - PullRequest
12 голосов
/ 30 января 2011

Очевидно, метод add_class от Nokogiri работает только на NodeList s, что делает этот код недействительным:

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 для отдельных узлов!

Ответы [ 2 ]

15 голосов
/ 30 января 2011

Класс 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"

Редактировать : Вы можете видеть, что это в основном то, что делает Нокогири для внутреннего метода 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
1 голос
/ 30 января 2011

Nokogiri's 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>.

...