256 Kilobytes

[PHP] How to Configure a Website to Show "Jump To" Links in the SERPs: Automatically Generating Heading ID Attributes and/or Tables of Contents

Articles in Search Engine Optimization | By August R. Garcia

Published | Last Update

You know this shit? The "Jump To" [Anchor] in SERP results? This seems obvious in retrospect, But, I bet you have to fucking have id="" attributes in your H1/2/3/4/5/6 tags to make that shit show up

885 views, 0 RAMs, and 1 comment

You know this shit? The "Jump To" [Anchor] in SERP results?

Screenshot of Google Search Result showing 'Jump to' Top 10 PHP Frameworks for Web Development

This seems obvious in retrospect, but, I bet you have to fucking have id="" attributes in your H1/2/3/4/5/6 tags to make that shit show up.

How Do Jump To Anchors Work in Search Results? 

According to a post from (lmao) 2009 on Google's Webmaster Blog: 

There are a few things you can do to increase the chances that they might appear on your pages. First, ensure that long, multi-topic pages on your site are well-structured and broken into distinct logical sections. Second, ensure that each section has an associated anchor with a descriptive name (i.e., not just "Section 2.1"), and that your page includes a "table of contents" which links to the individual anchors. The new in-snippet links only appear for relevant queries, so you won't see it on the results all the time — only when we think that a link to a section would be highly useful for a particular query.


Notably, the snippet seems to indicate that a TOC must be included in the actual page for this to work, which would suggest that adding IDs without an on-page TOC for the purpose of affecting search results would not have the same result. Although that might be bullshit, and was also posted in 2009. If anyone has information that would suggest that the TOC is not required, feel free to post it in the comments.

Also, this is the "Jump To" link from the SERP screenshot:

As you can see, the article has a table of contents and the specific heading has a relevant anchor:

  • <h2 id="top-10-php-frameworks-for-web-development">Top 10 PHP Frameworks for Web Development</h2>

PHP Code to Automatically Insert ID Attributes for Headings

Manually creating anchors for every heading is impractical. You can do this relatively easily by passing your pages' HTML code through a function like the generate_toc() function shown below:

[PHP] Creating Valid ID Attributes for Headings

For the main PHP function to work, you'll need to be able to convert arbitrary headings to valid ID strings, such as "Nice Dogs" to "nice-dogs," which involves handling various edge cases, such as special characters and so on. This function handles this aspect of the code:

// Determine the correct URL slug (after the [content/user/other] ID)
// This automatically changes and redirects if the thread title changes
function vanity_slug($str) {
        // Decode chars so they can be removed
        // (e.g., &gt; contains alphanumeric characters; if not encoded, the 'g' and 't' will not be parsed out)
        $str = htmlspecialchars_decode($str)              ;

        // Handle single quotes simlarly to above
        $str = str_replace("&#039;", "", $str)            ;

        // Remove trailing spaces
        $str = trim($str)                                 ;

        // Replace spaces with dashes
        $str = str_replace(' ', '-', strtolower($str))    ;

        // Limit the slug to a maximum of 255 characters
        $str = str_limit($str, 255)                       ;

        // Remove apostrophes
        $str = str_replace("'", "", $str) ;

        // Replace non alphanumeric characters with dashes
        $str = preg_replace("/[^A-Za-z0-9-]/", "-", $str) ;

        // Remove duplicate dashes
        $str = preg_replace('/-+/', '-', $str)            ;

        // Remove trailing dashes
        $str = trim($str, '-')                            ;

        return $str;

If you're using Laravel, you can also use the built in function named str_slug() that does roughly the same thing as the code above:

[PHP] Generating a Table of Contents and Adding Heading ID Attributes

// @param $html      A string of HTML 
// @return           A string of HTML with ids added to the H1, H2, and H3 tags.
//                   Also echos out a table of contents for the H1, H2, and H3 tags in question.
//                   If you don't want to do that / just want the heading IDs, then comment
//                   out or modify the three lines with "(!)" next to them.
function generate_toc($html) {

        // Prevent XML warnings from breaking your site 
        //   https://stackoverflow.com/questions/11819603/dom-loadhtml-doesnt-work-properly-on-a-server libxml_use_internal_errors(true);

        // Convert the HTML string to a DOMDocument object for non-retarded parsing: 
        $doc = new DOMDocument();

        // Get the headings. Shove h4, h5, h6, etc headings in here if you want those done as well.
        $xpath = new DOMXpath($doc);
        $t     = $xpath->query("//h1 | //h2 | //h3");

        // To store the HTML used for the TOC 
        $toc   = "";
        // Heading IDs must be unique
        // Keep track of the headings created so that you can append a number
        // to the anchor if there are identical headings 
        $ids_used = [];
        for ($iii = 0; $iii < $t->length; $iii++) {

                // The current heading <h1>/whatever node
                $node = $t->item($iii);

                // Figure out the heading depth so that this can be indicated
                // in the TOC for styling purposes:
                $class = htmlspecialchars($node->tagName)                   ;   // (!)
                // Remvoe any tags nested withing the heading, such as if you
                // have a heading like:
                // <h1>This is a <em>nice</em> post</h1>
                $val   = strip_tags(DOMinnerHTML($node))  ;

                // Create the actual ID attribute.
                // "A Nice Post" --> "a-nice-post"
                $id    = vanity_slug( $val )      ;       

                // If the vanity slug is an empty string, such as if there are
                // no alphanumeric characters in the title, use a default
                if (!$id)
                        $id = "section"; 

                // If this ID attribute has already been used
                // (such as if you have multiple sections that have the same subheadings),
                // then append a number to make the ID unique
                if (in_array($id, $ids_used)) {
                        $count = 0;
                        foreach ($ids_used as $previously_used_id)
                                $count += ($id == $previously_used_id) ? 1       :  0;
                        $ids_used[]  = $id;  
                        $id         .= "-" . $count; 
                } else $ids_used[] = $id;  

                // Create the list item for the link to the section's anchor via TOC
                $toc .= "<li class=\"$class\"><a href=\"#$id\">".$val."</a></li>";  // (!)

                // Add IDs to the Headings
                $node->setAttribute('id', $id);
        if (isset($toc) && $toc)    echo "<ul class='toc'>$toc</ul>"; // (!)

        // Get the HTML string out of the DOMDocument object
        $html = $doc->saveHTML(); 

        return $html;

// Helper function: 
//   https://stackoverflow.com/questions/2087103/how-to-get-innerhtml-of-domnode
function DOMinnerHTML(DOMNode $element)
    $innerHTML = "";
    $children  = $element->childNodes;

    foreach ($children as $child)
        $innerHTML .= $element->ownerDocument->saveHTML($child);
    return $innerHTML;

In Conclusion

It's probably even useful to create tables of contents in your webpages. Also, probably you increase traffic/click through rate to some extent by doing this, because it will probably create "Jump To [Section]" anchors in search results.

You can even use this code, if that's something that you want to do.

Download more RAM. 🐏 ⨉ 0Posted by August R. Garcia 2 years ago

Edit History

• [2019-01-24 20:24 PST] August R. Garcia (2 years ago)
• [2019-01-24 20:24 PST] August R. Garcia (2 years ago)
• [2019-01-24 20:24 PST] August R. Garcia (2 years ago)
🕓 Posted at 24 January, 2019 20:24 PM PST

Read More:
Profile Photo - August R. GarciaAugust R. GarciaLARPing as a Sysadmi...Portland, ORSite Owner

August Garcia is some guy who used to sell Viagra on the Internet. He made this website to LARP as a sysadmin while posting about garbage like user-agent spoofing, spintax, the only good keyboard, virtual assitants from Pakistan, links with the rel="nofollow" attributeproxiessin, the developer console, literally every link building method, and other junk.

Available at arg@256kilobytes.com, via Twitter, or arg.256kilobytes.com. Open to business inquiries based on availability.

Profile Photo - August R. GarciaAugust R. GarciaLARPing as a Sysadmi...Portland, ORSite Owner

Also, if you're trying to create a table of contents on WordPress (or Joomla or Drupal or some other CMS), there are probably plugins that will handle this:

Make sure that they have the two required features (TOC and descriptive anchors) and would imagine that these will work fine for this.

Download more RAM. 🐏 ⨉ 0Posted by August R. Garcia 2 years ago 🕓 Posted at 25 January, 2019 00:50 AM PST

Sir, I can do you a nice SEO.

Post a New Comment

Do you like having a good time?

Register an Account

You can also login to an existing account or reset your password. All use of this site is subject to the terms of service and privacy policy.

Read Quality Articles

Read some quality articles. If you can manage to not get banned for like five minutes, you can even post your own articles.

View Articles →

Argue with People on the Internet

Use your account to explain why people are wrong on the Internet forum.

View Forum →

Vandalize the Wiki

Or don't. I'm not your dad.

View Wiki →

Ask and/or Answer Questions

If someone asks a terrible question, post a LMGTFY link.

View Answers →

Make Some Money

Hire freelancers and/or advertise your goods and/or services. Hire people directly. We're not a middleman or your dad. Manage your own business transactions.

Register an Account