Two nots in CSS selector with Nokogiri - css

Right now I have a selector working with jQuery as follows:
.original-tweet:not([data-is-reply-to="true"],.retweeted)
However this doesn't seem to work using the Nokogiri gem in ruby:
doc.css('.original-tweet:not([data-is-reply-to="true"],.retweeted)')
The above causes a cash, but each of the parts of the not independently work:
doc.css('.original-tweet:not([data-is-reply-to="true"])')
and
doc.css('.original-tweet:not(.retweeted)')
What's the best way to actually get the selector I want. Is this something that just isn't supported in nokogiri?

Okay, I solved it with XPATH
The following worked (note: the xpath I created was entirely computer generated)
doc.xpath("//*[contains(concat(' ', #class, ' '), ' original-tweet ') and not(#data-is-reply-to = \"true\") and not(#data-retweet-id)]")
Edit: further inspection shows that this is still selecting items with the retweeted class (turns out this was a false assumption on my part, I should have been looking for the data-retweet-id attribute instead of the retweet class)
github.com/sparklemotion/nokogiri/issues/451 - this issue relates to why I needed to use xpath here.

While the selector may work with jQuery, it's not a valid CSS selector:
> $$('.original-tweet:not([data-is-reply-to="true"], .retweeted)')
Error: SyntaxError: DOM Exception 12
.original-tweet:not([data-is-reply-to="true"]):not(.retweeted) should work.

For now a possible workaround might be:
doc.css('.original-tweet:not([data-is-reply-to="true"])') - doc.css('.retweeted')

Related

Style Lint : Max Specificity Selector with Regex

I have a problem with Style Lint. I have set the selector-max-specificity at 0,2,0 with some ignored rules : ignoreSelectors: [":focus", ":hover", etc..]
Now, I want to ignore the :not(.myClass) selector but this doesn't work :(
For example i have my class with this name : .track a:not(.track-visual)
I have tried with regex selector "/:not\\(.*\\)/" or even with "/.*:not\\(.*\\)/"
But no chance :'(
If you have a solution, i'm interested :)
Thank you
I'm guessing that here we would be wishing to capture those with :not, which in that event, we would be starting with a simple expression such as:
.+?:not\(.+?\)
Demo
RegEx Circuit
jex.im visualizes regular expressions:
Don't think this can be done. The latest code for the rule includes the following:
const ownSpecificity =
ownValue === ':not' || ownValue === ':matches'
? // :not and :matches don't add specificity themselves, but their children do
zeroSpecificity()
: simpleSpecificity(ownValue);
As per the comment, this is not including the :not itself as something that would add specificity but is including its children, which is what the regex would be checking.

How to use `&` and a tag on the same selector

I am trying to write a nested selector that selects a certain tag that has a certain attribute, for example
<li foo="bar">
To select this, li[foo="bar"] would work, but I want to nest it under [foo="bar"] using the scss & notation because I have other things with the [foo="bar"] attribute (e.g., <div foo="bar" class="baz">), and I want to group them together. When I try:
[foo = "bar"]{
&li{
...
}
&.baz{
...
}
}
It returns an error that says li may only be used at the beginning of a compound selector, and if I try:
[foo = "bar"]{
li&{
...
}
&.baz{
...
}
}
then it says & may only be used at the beginning of a compound selector. How can I do this correctly?
The right syntax nowadays would be li#{&}.
Last I heard, this is actually an upcoming feature in SASS 3.3 or 3.4.
See this thread for a question similar to yours and this thread for the proposed solution to be included (which, at the time of writing, seems to be &__element).
The issue here isn't the use of [] and & together - it's the use of a plain element in the selector. Your example with .baz should work as expected.

How can I locate a tag via CSS selectors, referencing the content of a sibling tag?

I'm working on a Ruby script that will parse and manipulate some XML files. I'm using Nokogiri for the XML handling.
The problem I have is that there are several constructs like this one:
<USER_ELEMENT>
<NAME>ATTRIBUTE01</NAME>
<VALUE>XXX</VALUE>
</USER_ELEMENT>
I need to set the <VALUE> tag that's within the same of a particular <VALUE>ATTRIBUEnn</VALUE>. My current approach is using
xml.css('USER_ELEMENT').find { |node| node.at_css('NAME').text == 'ATTRIBUTEnn'}.at_css('VALUE').content = 'NEW_VALUE'
but it looks rather ugly.
I'm wondering which would be a cleaner way of dealing with the situation?
Using XPath:
attnn = "ATTRIBUTE01"
xml.at_xpath("//USER_ELEMENT[NAME='#{attnn}']/VALUE").content = "Yay"
puts xml
#=> <USER_ELEMENT>
#=> <NAME>ATTRIBUTE01</NAME>
#=> <VALUE>Yay</VALUE>
#=> </USER_ELEMENT>
In English, that XPath says:
//USER_ELEMENT - find elements with this name anywhere in the document
[…] - but only if…
NAME="ATTRIBUTE01" - …you can find a child NAME element with this text
/VALUE - and now find the child VALUE elements of these
The css selector for siblings is ~:
xml.at('USER_ELEMENT > NAME[text()="ATTRIBUTE01"] ~ VALUE').content = 'NEW_VALUE'
I don't know if nokogiri supports CSS3, but if it does, this should work
xml.css('USER_ELEMENT NAME:content("ATTRIBUTEnn") + VALUE').content = "NEW_VALUE"

Where is keywords code assist and letter not allowed problem(Aptana Studio 3)

I recently switched from Aptana2 to version3.0.3, and the first thing i did was to install the sdomcl. file to get jQuery code assist.It works fine for jQuery, but there is no code assist for many keywords.For exymple there is no support for var,while,throw,try,break,case,catch etc.
Also there is no function, instead intellisense sugests Function.
The second problem is that i am constantly getting this warning '<' + '/' +letter not allowed here when typing something valid like this:
confirmDiv =$("")-sorry for this,but it wont let me type what i want, basically i am just creating a new div with the correct syntax.
Could it be something with Html Tidy?Anyways, big thanks in advance!
Aptana Studio 3.0.4 includes code assist for JavaScript keywords.
I've read that for Javascript the slashes / must be escaped with backslashes \ as it says here
Doing so the warning dissapears ;)

Is it possible to parse a stylesheet with Nokogiri?

I've spent my requisite two hours Googling this, and I can not find any good answers, so let's see if humans can beat Google computers.
I want to parse a stylesheet in Ruby so that I can apply those styles to elements in my document (to make the styles inlined). So, I want to take something like
<style>
.mystyle {
color:white;
}
</style>
And be able to extract it into a Nokogiri object of some sort.
The Nokogiri class "CSS::Parser" (http://nokogiri.rubyforge.org/nokogiri/Nokogiri/CSS/Parser.html) certainly has a promising name, but I can't find any documentation on what it is or how it works, so I have no idea if it can do what I'm after here.
My end goal is to be able to write code something like:
a_web_page = Nokogiri::HTML(html_page_as_string)
parsed_styles = Nokogiri::CSS.parse(html_page_as_string)
parsed_styles.each do |style|
existing_inlined_style = a_web_page.css(style.declaration) || ''
a_web_page.css(style.declaration)['css'] = existing_inlined_style + style.definition
end
Which would extract styles from a stylesheet and add them all as inlined styles to my document.
Nokogiri can't parse CSS stylesheets.
The CSS::Parser that you came across parses CSS expressions. It is used whenever you traverse a HTML tree by CSS selectors rather than XPath (this is a cool feature of Nokogiri).
There is a Ruby CSS parser, though. You can use it together with Nokogiri to achieve what you want.
require "nokogiri"
require "css_parser"
html = Nokogiri::HTML(html_string)
css = CssParser::Parser.new
css.add_block!(css_string)
css.each_selector do |selector, declarations, specificity|
html.css(selector).each do |element|
style = element.attributes["style"]&.value || ""
element.set_attribute('style', [style, declarations].compact.join(" "))
end
end
#molf definitely had a great start there, but it still required debugging a handful of problems to get it working in production. Here is the current, tested version of this:
html = Nokogiri::HTML(html_string)
css = CssParser::Parser.new
css.add_block!(html_string) # Warning: This line modifies the string passed into it. In potentially bad ways. Make sure the string has been duped and stored elsewhere before passing this.
css.each_selector do |selector, declarations, specificity|
next unless selector =~ /^[\d\w\s\#\.\-]*$/ # Some of the selectors given by css_parser aren't actually selectors.
begin
elements = html.css(selector)
elements.each do |match|
match["style"] = [match["style"], declarations].compact.join(" ")
end
rescue
logger.info("Couldn't parse selector '#{selector}'")
end
end
html_with_inline_styles = html.to_s

Resources