acidzombie24 acidzombie24 - 3 days ago 4
C# Question

JQuery ".closest()" equivalent in HtmlAgilityPack?

I'm using

HtmlAgilityPack
. Does it have a function similar to
jQuery
closest
? (closest parent that matches a
css
selector). I tried google and the website http://htmlagilitypack.codeplex.com/ and both doesn't appear to have an answer.

Answer

As there is no built-in method currently, you can write a Extension method to achieve this.

I have written a simple extension method which can be used to find elements with tagName, ID and class names that you can use.

Anyways it can be further extended easily to match other selectors.

public static class HtmlAgilityPackExtensions
{
    public static HtmlNode Closest(this HtmlNode node, string jQuerySelector)
    {
        if (node == null) return null;
        string tagName = "", id = "";
        var classes = new List<string>();

        if (jQuerySelector.Contains("."))
        {
            var parts = jQuerySelector.Split('.');

            if (!string.IsNullOrWhiteSpace(parts[0]))
            {
                tagName = parts[0];
            }

            for (int i = 1; i < parts.Length; i++)
            {
                classes.Add(parts[i]);
            }
        }

        if (jQuerySelector.Contains("#"))
        {
            var parts = jQuerySelector.Split('#');

            if (!string.IsNullOrWhiteSpace(parts[0]))
            {
                tagName = parts[0];
            }

            id = parts[1];
        }

        if (string.IsNullOrWhiteSpace(tagName) && string.IsNullOrWhiteSpace(id) && classes.Count == 0)
        {
           tagName = jQuerySelector;
        }

        HtmlNode closestParent = null;

        while (node.ParentNode != null && closestParent == null)
        {
            var isClosest = true;
            node = node.ParentNode;

            if (!string.IsNullOrWhiteSpace(tagName))
            {
                isClosest = node.Name == tagName;
            }

            if (isClosest && !string.IsNullOrWhiteSpace(id))
            {
                isClosest = node.Id == id;
            }

            if (isClosest && classes.Count > 0)
            {
                var classNames = node.GetAttributeValue("class", "");
                if (!string.IsNullOrWhiteSpace(classNames))
                {
                    foreach (string c in classes)
                    {
                        isClosest = classNames.Contains(c);
                        if (!isClosest) break;
                    }
                }
            }

            if (isClosest)
            {
                closestParent = node;
            }
        }

        return closestParent;
    }
}

Test Code

       var html = "<div><div id='parent1' class='parent'><span id='parent2' class='parent'><div id='parent3' class='parent'><div id='TestNode' class='child'>Test node</div></div></span></div></div>";
        var htmlDoc = new HtmlDocument();
        htmlDoc.LoadHtml(html);

        var testNode1 = htmlDoc.DocumentNode.SelectSingleNode("//div[@id='TestNode']");
        if (testNode1 != null)
        {
            var parent1 = testNode1.Closest(".parent");
            var parent2 = testNode1.Closest("#parent1");
            var parent3 = testNode1.Closest("span.parent");
            var nonExistingParent = testNode1.Closest("span.parent1");
        }
Comments