2011-01-30 19 views
10

A quanto pare il metodo di Nokogiri add_class funziona solo su NodeList s, rendendo questo codice non valido:Aggiungere una classe ad un elemento con Nokogiri

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

Che cosa posso fare per fare questo lavoro di codice? Ho pensato che sarebbe stato qualcosa come

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

ma questo non funziona neanche. Per favore dimmi che non devo implementare il mio add_class per i singoli nodi!

risposta

12

una classe CSS è solo un altro attributo di un elemento:

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

Dal classi CSS sono delimitato da spazi nell'attributo, se non siete sicuri se potrebbero già esistere una o più classi Avrete bisogno di qualcosa come

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

È necessario impostare in modo esplicito l'attributo utilizzando = invece di mutare la stringa restituita per l'attributo. Questo, ad esempio, non cambierà il DOM:

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

Anche se si traduce in più lavoro svolto, probabilmente sarei fare questo in questo modo:

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

Se non si desidera di scimmia-patch della classe, si potrebbe in alternativa:

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" 

Modifica: si può vedere che questo è fondamentalmente ciò che fa Nokogiri internamente per la add_class metodo che hai trovato cliccando sulla classe per visualizzare la fonte:

# 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 
+0

bigup il ClassMutator! Grazie! – flunder

1

Nokogiri di add_class, lavora su una serie di nodi, come hai trovato. Cercando di aggiungere la classe all'interno del blocco each non funzionerebbe però, perché a quel punto si sta lavorando su un singolo nodo.

Invece:

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 

quali uscite:

# >> 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> 

Il metodo tap, realizzato in Ruby 1.9+, dà accesso al NodeList stesso, permettendo il metodo add_class per aggiungere il "boo" classificare i tag <p>.

+2

Perché non puoi semplicemente fare 'doc.search ('p'). Add_class ('boo'). Each do ...' –

+1

Puoi usare 'doc = Nokogiri :: HTML.fragment (html)' se non voglio che Nokogiri aggiunga il doctype e altri tag html e body – Archonic

Problemi correlati