1

I am using Nokogiri to try to detect in the page if there are elements with similar class. Unfortunately, the class involved a number--which I found difficulty to work with Nokogiri. The problem is that, Nokogiri always error when the class name begin with number.

This is the error backtrace that I got:

Nokogiri::CSS::SyntaxError: unexpected '.0' after '[#<Nokogiri::CSS::Node:0x007fced40a3ef8 @type=:CONDITIONAL_SELECTOR, @value=[#<Nokogiri::CSS::Node:0x007fcecdf502a0 @type=:ELEMENT_NAME, @value=["tr"]>, #<Nokogiri::CSS::Node:0x007fced40a3f70 @type=:COMBINATOR, @value=[#<Nokogiri::CSS::Node:0x007fcecdf50188 @type=:CLASS_CONDITION, @value=["classA"]>, #<Nokogiri::CSS::Node:0x007fcecdf518d0 @type=:CLASS_CONDITION, @value=["classB"]>]>]>]'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/nokogiri-1.6.3.rc3/lib/nokogiri/css/parser_extras.rb:87:in `on_error'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/2.1.0/racc/parser.rb:258:in `_racc_do_parse_c'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/2.1.0/racc/parser.rb:258:in `do_parse'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/nokogiri-1.6.3.rc3/lib/nokogiri/css/parser_extras.rb:62:in `parse'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/nokogiri-1.6.3.rc3/lib/nokogiri/css/parser_extras.rb:79:in `xpath_for'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/nokogiri-1.6.3.rc3/lib/nokogiri/css.rb:23:in `xpath_for'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/nokogiri-1.6.3.rc3/lib/nokogiri/xml/node.rb:211:in `block in css'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/nokogiri-1.6.3.rc3/lib/nokogiri/xml/node.rb:210:in `map'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/nokogiri-1.6.3.rc3/lib/nokogiri/xml/node.rb:210:in `css'
    from (irb):106
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/railties-4.1.1/lib/rails/commands/console.rb:90:in `start'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/railties-4.1.1/lib/rails/commands/console.rb:9:in `start'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/railties-4.1.1/lib/rails/commands/commands_tasks.rb:69:in `console'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/railties-4.1.1/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/railties-4.1.1/lib/rails/commands.rb:17:in `<top (required)>'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:247:in `require'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:247:in `block in require'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:232:in `load_dependency'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:247:in `require'
    from /Users/works/Documents/OF/RUBY-211/indoskyapi/bin/rails:9:in `<top (required)>'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:241:in `load'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:241:in `block in load'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:232:in `load_dependency'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activesupport-4.1.1/lib/active_support/dependencies.rb:241:in `load'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from /Users/works/.rbenv/versions/2.1.1/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from -e:1:in `<main>'

The code that I used was:

pg.css("tr.classA.classB.0")      

EDIT: I thought the question can be extracted out from my 'question' that is: how can I use number as class selector in nokogiri .css function.

user3551335
  • 187
  • 2
  • 10

2 Answers2

2

This question is similar to another recent question. That question asked about ids though, the solution for classes is slightly different.

The problem is that although 0 is valid as an HTML class value, it’s not valid as a CSS class selector as they cannot start with a number.

You can work around this using the [att~=val] attribute selector like this:

pg.css("tr.classA.classB[class~='0']")

This will match all tr elements that are in all the classes classA, classB and 0.

Community
  • 1
  • 1
matt
  • 78,533
  • 8
  • 163
  • 197
  • Actually, `0` is [not a legal HTML class name](http://www.w3.org/TR/CSS21/syndata.html#characters); class names may not start with a digit. +1, however, for providing the attribute-selector workaround that will work with HTML that is invalid in some sense, and for using the right attribute selector. – Phrogz Aug 05 '14 at 19:56
  • @Phrogz My reading is that `0` is valid [_in the HTML_](http://www.w3.org/TR/html5/dom.html#classes), but not valid _in the CSS selector syntax_. In other words a CSS class selector (e.g. `.foo`) can only select a subset of valid class names. Reading directly from the specs can be tricky so I could have got it wrong but tests with the validators ([HTML](http://validator.w3.org/) and [CSS](http://jigsaw.w3.org/css-validator/)) seem to support me. – matt Aug 05 '14 at 20:35
  • can you explainn why class ~= is required? what that stands for? I aware =~ can be used to find by using regex expression. but I afraid the case is different here. maybe if you explain a bit we can see the difference between your solution pg.css("tr[class ~= 'classA classB 0']"), and, pg.css("tr[class = 'classA classB 0']") – user3551335 Aug 06 '14 at 00:31
  • 1
    @user3551335 did you follow the link? “`[att~=val]` Represents an element with the `att` attribute whose value is a whitespace-separated list of words, one of which is exactly ‘val’.” Using `~=` you can select a single class. Using `=` means you can only check the entire string for an exact match. – matt Aug 06 '14 at 01:09
  • Sorry for my ignorance. – user3551335 Aug 06 '14 at 02:03
1

In CSS, identifiers cannot start with a digit. I never used Nokogiri but I assume it follows this principle by not allowing you to use such identifier in your selector. Here is a more elaborated answer about allowed characters in CSS identifiers: https://stackoverflow.com/a/449005

Community
  • 1
  • 1
  • 1
    It's possible to use xpath to select classes that begins with a digit. – Linus Oleander Aug 05 '14 at 13:29
  • sadly some people never read the documentation. and browser never brave enough to alert developers for any breach of code until there is developer that dare enough to use plain number. ignorance at its best but well... it is been in the real world case, not ideal world we are talking here. – user3551335 Aug 05 '14 at 23:18