HTML and the DOM

edit

A document is a tree of nodes: the DOM of the browsers, with the same types and the same members, named the .NET way. Parse markup into a document, find the nodes you need with CSS selectors, XPath or LINQ, change the tree, then write it back as HTML.

var document = HtmlDocumentParser.ParseDocument("""
    <ul>
      <li><a href="/a">A</a>
      <li><a href="/b">B</a>
    </ul>
    """);
var list = document.QuerySelector("ul")!;

// Create elements
var item = document.CreateElement<HtmlLIElement>();
var link = document.CreateElement<HtmlAnchorElement>();
link.Href = "/c";
link.TextContent = "C";
link.ClassList.Add("important");
link.SetAttribute("data-id", "3");
item.Append(link);
list.Append(item);

// Parse markup into an element, edit the styles, remove nodes
list.InsertAdjacentHtml("beforeend", "<li><a href=/d>D</a>");
link.Style.SetProperty("text-decoration", "underline");
document.QuerySelector("a[href='/b']")?.Remove();

// Serialize the document
var html = document.GetHtml();
var indented = document.ToHtml(new HtmlPrettyFormatter());
var minified = document.ToHtml(new HtmlMinifyFormatter());
Assert.StartsWith("<html><head></head><body><ul>", html, StringComparison.Ordinal);
Assert.Equal("""
    <html>
      <head></head>
      <body>
        <ul>
          <li>
            <a href="/a">A</a>
          </li>
          <li></li>
          <li>
            <a href="/c" class="important" data-id="3" style="text-decoration: underline;">C</a>
          </li>
          <li>
            <a href="/d">D</a>
          </li>
        </ul>
      </body>
    </html>
    """, indented);
Assert.Equal("""<ul><li><a href=/a>A</a><li><li><a href=/c class=important data-id=3 style="text-decoration: underline;">C</a><li><a href=/d>D</a></ul>""", minified);

Documents with and without a browsing context

HtmlDocumentParser and XmlDocumentParser create documents that are only trees: no window, no event loop, no computed style and no layout. They are the fastest way to read or transform markup. Everything on the pages of this group works on them, except the features that need a page:

FeatureNeeds
Computed styles, media queries, layout and geometryA browsing context
Custom elements of the global registryA browsing context (or a registry given to the parser)
The callbacks of mutation observers, the load eventsThe event loop of a browsing context
Scripts and event handler attributesA browsing context with a script engine

To get a document with a browsing context, navigate a BrowsingContext to the markup: context.Navigate("https://example.com/", _ => html) returns the document.

Naming

The members have the names of the web API, adapted to the .NET guidelines: querySelector is QuerySelector, innerHTML is InnerHtml, classList is ClassList. Each element has its typed interface (HtmlAnchorElement, HtmlInputElement, SvgCircleElement...), with the reflected attributes as properties (Href, Value, Disabled).

In this section