Html Agility Pack provides powerful methods to dynamically add new nodes to existing HTML documents in C#. You can create various types of nodes (elements, text, comments) and insert them at specific locations within your HTML structure.
Basic Node Addition Process
The process involves four main steps:
- Load the HTML document into an
HtmlDocument
object - Create the new node using appropriate methods
- Find the parent or reference node where you want to insert
- Insert the node using positioning methods
Installation
First, install Html Agility Pack via NuGet:
Install-Package HtmlAgilityPack
Or using .NET CLI:
dotnet add package HtmlAgilityPack
Creating Different Types of Nodes
Element Nodes
using HtmlAgilityPack;
// Method 1: Create from HTML string
HtmlNode elementNode = HtmlNode.CreateNode("<div class='new-content'>Hello World</div>");
// Method 2: Create element programmatically
HtmlNode divNode = htmlDoc.CreateElement("div");
divNode.SetAttributeValue("class", "new-content");
divNode.InnerText = "Hello World";
// Method 3: Create complex nested structure
HtmlNode complexNode = HtmlNode.CreateNode(@"
<div class='card'>
<h3>Card Title</h3>
<p>Card content goes here.</p>
<button>Click Me</button>
</div>");
Text Nodes
// Create a text node
HtmlTextNode textNode = htmlDoc.CreateTextNode("This is plain text content");
Comment Nodes
// Create a comment node
HtmlCommentNode commentNode = htmlDoc.CreateComment("This is a comment");
Node Insertion Methods
AppendChild - Add to End
using HtmlAgilityPack;
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(@"
<html>
<body>
<div id='container'>
<p>Existing paragraph</p>
</div>
</body>
</html>");
// Find the container
HtmlNode container = htmlDoc.GetElementbyId("container");
// Create and append new node
HtmlNode newParagraph = HtmlNode.CreateNode("<p>This will be added at the end</p>");
container.AppendChild(newParagraph);
// Result: New paragraph appears after existing content
PrependChild - Add to Beginning
// Add to the beginning of the container
HtmlNode firstParagraph = HtmlNode.CreateNode("<p>This will be added at the beginning</p>");
container.PrependChild(firstParagraph);
// Result: New paragraph appears before existing content
InsertAfter - Add After Specific Node
// Find a specific node to insert after
HtmlNode existingParagraph = container.SelectSingleNode(".//p[1]");
// Create new node and insert after the existing one
HtmlNode afterNode = HtmlNode.CreateNode("<p>This comes after the first paragraph</p>");
container.InsertAfter(afterNode, existingParagraph);
InsertBefore - Add Before Specific Node
// Insert before a specific node
HtmlNode beforeNode = HtmlNode.CreateNode("<p>This comes before the first paragraph</p>");
container.InsertBefore(beforeNode, existingParagraph);
Complete Working Example
using HtmlAgilityPack;
using System;
class Program
{
static void Main()
{
// Load HTML from string
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(@"
<html>
<head><title>Test Page</title></head>
<body>
<div id='content'>
<h1>Original Content</h1>
<p>Existing paragraph</p>
</div>
</body>
</html>");
// Find the content container
HtmlNode contentDiv = htmlDoc.GetElementbyId("content");
if (contentDiv != null)
{
// Add a new section with multiple elements
HtmlNode newSection = HtmlNode.CreateNode(@"
<div class='new-section'>
<h2>Added Section</h2>
<p>This section was added dynamically.</p>
<ul>
<li>First item</li>
<li>Second item</li>
</ul>
</div>");
contentDiv.AppendChild(newSection);
// Add a navigation menu to the beginning
HtmlNode navMenu = HtmlNode.CreateNode(@"
<nav>
<a href='#home'>Home</a>
<a href='#about'>About</a>
<a href='#contact'>Contact</a>
</nav>");
contentDiv.PrependChild(navMenu);
// Insert a divider after the original h1
HtmlNode h1 = contentDiv.SelectSingleNode(".//h1");
if (h1 != null)
{
HtmlNode divider = HtmlNode.CreateNode("<hr class='divider'>");
contentDiv.InsertAfter(divider, h1);
}
}
// Add meta tags to head
HtmlNode head = htmlDoc.DocumentNode.SelectSingleNode("//head");
if (head != null)
{
HtmlNode metaViewport = HtmlNode.CreateNode("<meta name='viewport' content='width=device-width, initial-scale=1.0'>");
HtmlNode metaCharset = HtmlNode.CreateNode("<meta charset='UTF-8'>");
head.PrependChild(metaCharset);
head.AppendChild(metaViewport);
}
// Save to file
htmlDoc.Save("modified-document.html");
// Display the result
Console.WriteLine("Modified HTML:");
Console.WriteLine(htmlDoc.DocumentNode.OuterHtml);
}
}
Advanced Techniques
Adding Nodes with Attributes
// Create node with multiple attributes
HtmlNode imageNode = htmlDoc.CreateElement("img");
imageNode.SetAttributeValue("src", "image.jpg");
imageNode.SetAttributeValue("alt", "Description");
imageNode.SetAttributeValue("class", "responsive-image");
imageNode.SetAttributeValue("loading", "lazy");
parentNode.AppendChild(imageNode);
Conditional Node Addition
// Add nodes based on conditions
HtmlNode targetDiv = htmlDoc.GetElementbyId("dynamic-content");
if (targetDiv != null)
{
// Check if specific content already exists
bool hasExistingContent = targetDiv.SelectSingleNode(".//div[@class='alert']") != null;
if (!hasExistingContent)
{
HtmlNode alertDiv = HtmlNode.CreateNode("<div class='alert'>Important notice!</div>");
targetDiv.PrependChild(alertDiv);
}
}
Bulk Node Addition
// Add multiple nodes efficiently
string[] items = { "Apple", "Banana", "Cherry", "Date" };
HtmlNode listContainer = htmlDoc.GetElementbyId("fruit-list");
foreach (string item in items)
{
HtmlNode listItem = htmlDoc.CreateElement("li");
listItem.InnerText = item;
listContainer.AppendChild(listItem);
}
Error Handling and Best Practices
try
{
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlContent);
// Always check if nodes exist before manipulating
HtmlNode targetNode = htmlDoc.GetElementbyId("target");
if (targetNode != null)
{
HtmlNode newNode = HtmlNode.CreateNode("<p>New content</p>");
// Validate the new node was created successfully
if (newNode != null)
{
targetNode.AppendChild(newNode);
// Save with error handling
try
{
htmlDoc.Save("output.html");
Console.WriteLine("Document saved successfully");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"Permission error: {ex.Message}");
}
}
}
else
{
Console.WriteLine("Target node not found");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error processing HTML: {ex.Message}");
}
Key Points to Remember
- Always check if parent nodes exist before adding children
- Use
CreateNode()
for HTML strings orCreateElement()
for programmatic creation - The document must be saved to persist changes
- Use XPath selectors with
SelectSingleNode()
orSelectNodes()
for precise targeting - Consider the document structure when choosing insertion methods (
AppendChild
,PrependChild
,InsertAfter
,InsertBefore
)
These methods provide complete control over HTML document manipulation, allowing you to build dynamic content generation and modification systems with Html Agility Pack.