Surround existing node with another node with Agility Pack

c# html-agility-pack

Question

How would you go about surrounding all tables with a <div class="overflow"></div> node? This apparently does not do it:

if (oldElement.Name == "table")
{
    HtmlDocument doc = new HtmlDocument();
    HtmlNode newElement = doc.CreateElement("div");
    newElement.SetAttributeValue("class", "overflow");
    newElement.AppendChild(oldElement);
    oldElement.ParentNode.ReplaceChild(newElement, oldElement);
}

Nothing happens to the tables when I try that code. But if i use:

if (oldElement.Name == "table")
{
    oldElement.Remove();
}

All tables are indeed removed, so I'm sure that i'm accessing the correct nodes.

Accepted Answer

It might be a little bit ugly, but you could just edit the InnerHtml attribute of the oldElement.ParentNode node like so:

if (oldElement.Name == "table")
{
    oldElement.ParentNode.InnerHtml = "\r\n<div class=\"overflow\">\r\n"
        + oldElement.OuterHtml +
        "\r\n</div>\r\n";
}

It also doesn't seem like you could edit the OuterHtml attribute of oldElement (which is why you have to get the ParentNode first). The HtmlAgilityPack says you can get/set OuterHtml, but VS2010 was telling me it's a read-only property.

Edit

I was playing around with some code to figure this out and saw that oldElement.ParentNode becomes the <div> node after AppendChild() is called. The solution I found is to make another HtmlNode at the start of the if block to hold the parent, and then calling ReplaceChild() on that node at the end:

if (oldElement.Name == "table")
{
    HtmlNode theParent = oldElement.ParentNode;

    HtmlDocument doc = new HtmlDocument();
    HtmlNode newElement = doc.CreateElement("div");
    newElement.SetAttributeValue("class", "overflow");
    newElement.AppendChild(oldElement);

    theParent.ReplaceChild(newElement, oldElement);
}

Popular Answer

Take a look at CsQuery, a C# port of jQuery. This can easily be accomplished. First load the document:

CQ doc = CQ.CreateFromFile(..)  // or
CQ doc = CQ.CreateFromUrl(..)   // or
CQ doc = CQ.Create(string)

Create the structure to wrap with, here are four different ways you could do that.

var wrap = CQ.CreateFragment("<div class='overflow'></div>");   // or

// you can pass HTML as a selector as in jQuery. The property indexer for a CQ
// object is the default method, same as $(..)

var wrap = doc["<div class='overflow'></div>"];   // or

var wrap = doc["<div />"].AddClass("overflow");  // or

// creates an element directly, sames as browser DOM methods

var wrap = doc.Document.CreateElement("div");
wrap.ClassName="overflow";

Wrap all tables with the structure:

doc["table"].Wrap(wrap);          // or use longhand for the Select method

doc.Select("table").Wrap(wrap);   

Render the complete document to a string:

string html = doc.Render();



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why