Overview
This project is a Scala implementation of the W3C Selectors Level 3 specification. I've already created one such implementation in Java, but Scala is my language of choice on the JVM nowadays.
Motivation
There's some built in support for XPath like expressions in the Scala library, but they are quite rudimentary. I also wanted to dig deeper into the parser combinators and this project seemed like a good way to do that.
Implementation details
The default implementation works with the XML classes provided by the standard Scala library, but the parser and its support classes may be used independent of the actual XML implementation.
Pseudo elements are parsed but not used when selecting nodes. It
doesn't make much sense to look for a::hover
etc outside of a web browser.
See example usage below for more info.
Supported selectors
*
any elementE
an element of type EE[foo]
an E element with a "foo" attributeE[foo="bar"]
an E element whose "foo" attribute value is exactly equal to "bar"E[foo~="bar"]
an E element whose "foo" attribute value is a list of whitespace-separated values, one of which is exactly equal to "bar"E[foo^="bar"]
an E element whose "foo" attribute value begins exactly with the string "bar"E[foo$="bar"]
an E element whose "foo" attribute value ends exactly with the string "bar"E[foo*="bar"]
an E element whose "foo" attribute value contains the substring "bar"E[foo|="en"]
an E element whose "foo" attribute has a hyphen-separated list of values beginning (from the left) with "en"E:root
an E element, root of the document or the root element specifiedE:nth-child(n)
an E element, the n-th child of its parentE:nth-last-child(n)
an E element, the n-th child of its parent, counting from the last oneE:nth-of-type(n)
an E element, the n-th sibling of its typeE:nth-last-of-type(n)
an E element, the n-th sibling of its type, counting from the last oneE:first-child
an E element, first child of its parentE:last-child
an E element, last child of its parentE:first-of-type
an E element, first sibling of its typeE:last-of-type
an E element, last sibling of its typeE:only-child
an E element, only child of its parentE:only-of-type
an E element, only sibling of its typeE:empty
an E element that has no children (including text nodes)E#myid
an E element with ID equal to "myid".E:not(s)
an E element that does not match simple selector sE F
an F element descendant of an E elementE > F
an F element child of an E elementE + F
an F element immediately preceded by an E elementE ~ F
an F element preceded by an E element
Example usage (for the default implementation)
Suppose you have a scala.xml.Elem
in a variable named elem
that
you'd like to query using CSS selectors:
val result = Selectors.query("div:nth-child(2n)", elem)
This will return an Either[String, List[Node]]
where Left
indicates a parser error. I.e. if the specified selector string
couldn't be parsed correctly.
It's also possible to query using a pre-parsed selector string. This is the best choice if querying more than once for the same selector string since it only needs to be parsed once.
val selectorGroups = SelectorParser.parse("div:nth-child(2n)") match {
case SelectorParser.Success(selectorGroups) => selectorGroups
case SelectorParser.Failure(msg) => error("Parse error: " + msg)
}
val nodes = Selectors.query(selectorGroups, elem)
By importing Selectors._
you can also query like this:
elem $ "div > div"
elem $ selectorGroups
elem.cssQuery("div > div")
elem.cssQuery(selectorGroups)
Both $
and cssQuery
returns a List[Node]
. If the selector string
specified causes a parser error an exception will be thrown. I think
this makes much more sense when the actual element is in focus.
Build instructions
This project uses SBT as its build tool.
Releases are synced to Maven central via Sonatype.
"se.fishtank" %% "css-selectors-scala" % version
The versions available can be found in the repo or by looking at the tags for this project.