Parse a snippet of HTML, such as table rows or list items, in the context of the element it belongs in, so you get the elements a browser would create and not a whole html and body document.
The HTML parser builds different trees for the same markup depending on where it sits. <tr><td> inside a tbody makes rows, while the same markup inside a div loses its table tags. Document.ParseFragment runs the fragment-parsing algorithm (the one behind innerHTML) with a context element you name.
using RecursiveAI.OneHtml; using RecursiveAI.OneHtml.Editing; // A fragment is parsed as if it were the inner HTML of a context element var tbody = new FragmentContext(ElementNamespace.Html, "tbody"); Document rows = Document.ParseFragment("<tr><td>Story time<td>Sat 10:30<tr><td>Repair café<td>Sun 14:00", tbody); Console.WriteLine($"Fragment: {rows.IsFragment}, rows: {rows.Root.QuerySelectorAll("tr").Count}"); Console.WriteLine(rows.SerializeChildren(rows.Root)); // The same markup in a <div> context: table parts without a table are dropped Document loose = Document.ParseFragment("<tr><td>Story time<td>Sat 10:30", new FragmentContext(ElementNamespace.Html, "div")); Console.WriteLine(loose.SerializeChildren(loose.Root)); // Take the context from an element of a real page Document page = Document.Parse(File.ReadAllBytes("events.html")); Node list = page.Root.QuerySelector("nav ul"); Document items = Document.ParseFragment("<li><a href=/kids/>Kids</a>", FragmentContext.FromElement(list)); Console.WriteLine(items.Root.QuerySelector("a").GetAttribute("href")); // Inserting markup into a page parses it in the target's context for you EditResult result = page.Apply(new[] { Edit.Append(list, "<li><a href=\"/kids/\">Kids</a></li>") }); Node newList = result.Document!.Root.QuerySelector("nav ul"); string written = result.Document.Source.Text[newList.Source.Range.Start..newList.Source.EndTag.End]; Console.WriteLine(written);
Fragment: True, rows: 2 <tr><td>Story time</td><td>Sat 10:30</td></tr><tr><td>Repair café</td><td>Sun 14:00</td></tr> Story timeSat 10:30 /kids/ <ul> <li><a href="/books/">Books</a></li> <li><a href="/events/" aria-current="page">Events</a></li> <li><a href='/visit/'>Visit</a></li> <li><a href="/kids/">Kids</a></li> </ul>
Notes:
new FragmentContext(ElementNamespace.Html, "tbody")names the context directly.FragmentContext.FromElement(node)takes it from an element of a parsed page, including whether it sits inside aformand the page’s quirks mode.- The result is a
DocumentwithIsFragmentset. ItsRootholds the parsed nodes, so you can query them as usual.SerializeChildren(Root)writes them back out as HTML. - You rarely need
ParseFragmentto insert markup.Edit.Append,Prepend,InsertBefore,InsertAfter,ReplaceandWrapalready parse the markup you pass in the target’s context. They also write it in the file’s own style: here the new<li>was put on its own line with the list’s indentation, and the single-quotedhref='/visit/'next to it is unchanged. - An element’s
<template>content is a separate fragment, reached throughnode.TemplateContent.
More on how inserted markup is placed and written: Editing.