Extensions and utilities for HtmlAgilityPack.
$ dotnet add package Universal.HtmlAgilityPackCSS selector support for HtmlAgilityPack, bringing familiar CSS selector querying to HTML parsing.
Converts CSS selectors to equivalent XPath expressions for use with HTML parsing libraries.
var converter = new CssSelectorToXPathConverter();
string xpath = converter.Convert("div.main p:first-child");
// Returns: ".//div[contains(concat(' ', normalize-space(@class), ' '), ' main ')]//p[position()=1]"
The converter generates relative XPath expressions (starting with ./) that search from the current context node, matching standard CSS selector scoping behavior.
Adds CSS selector support to HtmlDocument and HtmlNode with familiar querySelector and querySelectorAll methods.
var doc = new HtmlDocument();
doc.LoadHtml("<div class='content'><p id='intro'>Hello</p><p>World</p></div>");
// Select single element
HtmlNode intro = doc.QuerySelector("#intro");
HtmlNode firstParagraph = doc.QuerySelector("div.content p");
// Select multiple elements
IEnumerable<HtmlNode> allParagraphs = doc.QuerySelectorAll("p");
IEnumerable<HtmlNode> contentDivs = doc.QuerySelectorAll("div.content");
// Works on any HtmlNode - queries are scoped to that node
HtmlNode contentDiv = doc.QuerySelector(".content");
HtmlNode nestedParagraph = contentDiv.QuerySelector("p:first-child");
// ↑ Only searches within contentDiv, not the entire document
Queries are properly scoped to the node they're called on, just like in browser JavaScript:
var doc = new HtmlDocument();
doc.LoadHtml(@"
<ul id='list1'><li><a>Link 1</a></li></ul>
<ul id='list2'><li><a>Link 2</a></li></ul>
");
var list1 = doc.QuerySelector("#list1");
var link = list1.QuerySelector("a"); // Returns "Link 1" only
// Searches only within list1, not the entire document
This implementation supports most CSS 4 constructs and returns actual HtmlNode references from the document (not copies), enabling direct manipulation of the DOM structure.