Try our conversational search powered by Generative AI!

Make all header tags linkable


I would like to add id attributes to all header tags for a specific site, so that all heading can be linked to.


<h1>Main heading</h1>

...should be changed to...

<h1 id="main-heading">Main heading</h1>

...that it can be linked to with #main-heading. Ideally this should work for all headings, including those in text properties

What is the best way of solving this? Middleware and regex-search-replace?

Apr 04, 2022 5:43

If I needed to solve this I would consider the following approach:

  • Add the ID into the header tags directly within the razor files (possibly using Humanizer to format the title text into kebab format).
  • Add a post publish event for XHtmlString fields that adds the ID to header tags at the point of saving the rich text content using something like the HTML Agility Pack.

Parsing the rendered document and updating all of the header tags as a middleware would probably be more costly for the page request.

Apr 05, 2022 10:33

We commonly do this on render through the views, not through middleware.

We prefer this over modifying the content stored to the database.

Main reason being that we would then still need to parse the HTML content to figure out which anchor links there are to be able to render something like a TOC list or other markup.

Apr 05, 2022 11:09

We had an RTE extension that might worth a shot in your case. This is not tested but should work fine.

using System.IO;
using System.Web.Mvc;
using EPiServer.Core;
using EPiServer.Editor;
using EPiServer.Web.Mvc.Html;
using HtmlAgilityPack;

namespace Ecommerce.Site.Extensions
    public static class XhtmlStringExtensions
        /// <summary>
        /// Parses the XhtmlString for Image tags and sets them to lazy load
        /// </summary>
        public static string FormatRichText(this XhtmlString html, ViewContext context)
            // html is null when the TinyMce is not initialize (creating new block etc.)
            if (html == null) return string.Empty;

            // Load up Epi's HtmlHelper and ask it to render results
            var hh = new HtmlHelper(context, new ViewPage());

            string epiRenderingResult;

            using (var writer = new StringWriter())
                hh.ViewContext.Writer = writer;
                epiRenderingResult = writer.ToString();

            if (PageEditing.PageIsInEditMode)
                return epiRenderingResult;

            // once results are rendered, load up HtmlAgilityPack and have it parse results

            var doc = new HtmlDocument();

            var headings = doc.DocumentNode.SelectNodes("//h1");
            if (headings == null)
                return epiRenderingResult;

            foreach (var heading in headings)
                heading.SetAttributeValue("id", $"{yourValue}");

            var outerHtml = doc.DocumentNode.OuterHtml;
            return outerHtml; // return out the new resulting HTML

The other way I could think of doing it on razor files itself if isn't not always coming through RTE. For all RTEs you can create an InitializationModule that looks for all XhtmlString usages and use this extension. 

Apr 06, 2022 11:02

I think it's not too bad to add it to the first H1 found using JS.

There you can also add tabindex="0" to make it focusable.

You should not have tabindex="0" in the markup from the server.

Apr 28, 2022 16:11
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.