Problem encountered when using multiple tags for the table of contents

My script generates a table of contents based on the headings h2 and h3. The issue arises when there is a span tag within an h2, causing the script to skip that heading and not create a link for it. I suspect the problem lies in the regular expression

/<h([\d]).*>\s*[\d]*\s?[.]?\s?([^<]+)<\/h([\d])>/gi
but I'm not proficient in regex to fix it.

$(document).ready(function() {
  var toc = "";
  var level = 0;
  var maxLevel = 3;

  if (document.getElementById("contents") != null) {

    document.getElementById("contents").innerHTML =
      document.getElementById("contents").innerHTML.replace(
        /<h([\d]).*>\s*[\d]*\s?[.]?\s?([^<]+)<\/h([\d])>/gi,
        function(str, openLevel, titleText, closeLevel) {

          if (openLevel > maxLevel) {
            return str;
          }

          if (openLevel > level) {
            toc += (new Array(2)).join("<ol>");
          } else if (openLevel < level) {
            toc += (new Array(level - openLevel + 1)).join("</ol>");
          }

          level = parseInt(openLevel);

          var anchor = titleText.replace(/[^a-zA-Z]+/g, "-");

          toc += "<li><a href=\"#" + anchor + "\">" + titleText +
            "</a></li>";

          return "<a name=\"" + anchor + "\">" +
            "<h" + openLevel + "\">" + str + "</h" + closeLevel + ">" + "</a>";
        }
      );

    if (level) {
      toc += (new Array(level + 1)).join("</ol>");
    }

    document.getElementById("toc").innerHTML += toc;
  }

});
.blink {
  animation: blinker 1s step-start infinite;
  color: #ff0000;
  font-weight: bold;
  font-size: 0.85em;
}

@keyframes blinker {
  50% {
    opacity: 0;
  }
}
<head>
  <script type="text/javascript" src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
</head>

<body>
  <div id="toc">
    <div id="contents">
      <h2>What is JS?</h2>
      <h2 style="margin-top:1em;">Python Jobs - <span class="blink">New!</span></h2>
      <h2 style="margin-top:1em;">
        What is Python?
      </h2><h3>Introduction</h3>
    </div>
  </div>
</body>

Answer №1

You have the ability to achieve this without using regular expressions

$(document).ready(function() {
  const toc = document.createElement('ol');

  if (document.getElementById("contents") != null) {
    document.getElementById("contents").append(toc);
    document.querySelectorAll("#contents h2").forEach((title) => {
      const item = document.createElement('li');
      const link = document.createElement('a');

      if (title.getAttribute('id') === null) {
        title.setAttribute('id', 'id-' + Math.random().toString(16).substr(2, 6));
      }

      link.innerText = title.innerText;
      link.setAttribute('href', '#' + title.getAttribute('id'));

      item.append(link);
      toc.append(item);
    });
  }
});
.blink {
  animation: blinker 1s step-start infinite;
  color: #ff0000;
  font-weight: bold;
  font-size: 0.85em;
}

@keyframes blinker {
  50% {
    opacity: 0;
  }
}
<head>
  <script type="text/javascript" src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
</head>

<body>
  <div id="toc">
    <div id="contents">
      <h2>What is JS?</h2>
      <h2 style="margin-top:1em;">Python Jobs - <span class="blink">New!</span></h2>
      <h2 style="margin-top:1em;">
        What is Python?
      </h2>
    </div>
  </div>
</body>


Edit

You still have the option to maintain the structure as before

$(document).ready(function() {
  const toc = document.createElement('ol');
  let last = toc;

  if (document.getElementById("contents") != null) {
    document.getElementById("contents").append(toc);
    document.querySelectorAll("#contents h2, #contents h3").forEach((title) => {
      const item = document.createElement('li');
      const link = document.createElement('a');

      if (title.getAttribute('id') === null) {
        title.setAttribute('id', 'id-' + Math.random().toString(16).substr(2, 6));
      }

      link.innerText = title.innerText;
      link.setAttribute('href', '#' + title.getAttribute('id'));

      item.append(link);

      if (title.tagName === 'H2') {
        toc.append(item);
        last = item;
      } else {
        let ol = last.querySelector('ol');
        
        if (!ol) {
          ol = document.createElement('ol');
          last.append(ol);
        }

        ol.append(item);
      }
    });
  }
});
.blink {
  animation: blinker 1s step-start infinite;
  color: #ff0000;
  font-weight: bold;
  font-size: 0.85em;
}

@keyframes blinker {
  50% {
    opacity: 0;
  }
}
<head>
  <script type="text/javascript" src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
</head>

<body>
  <div id="toc">
    <div id="contents">
      <h2>What is JS?</h2>
      <h3>A programing language</h3>
      <h2 style="margin-top:1em;">Python Jobs - <span class="blink">New!</span></h2>
      <h2 style="margin-top:1em;">
        What is Python?
      </h2>
      <h3>A cool programing language</h3>
    </div>
  </div>
</body>

Answer №2

This code snippet is used to create an HTML collection of all the h2 tags within the .contents div.

It then goes through each item in the collection, extracting the .innerText (ignoring any other tags) and creating a link from the text with non-alphabet characters replaced by dashes.

If you hover over the generated links and check the status bar, you will see that they are valid links. You might need to adjust the .replace method to meet specific formatting requirements, but this code provides a good starting point.

let h2Collection = document.getElementById('contents').getElementsByTagName('h2');
// h2Collection represents an array-like collection of h1 elements found in the #contents div;
 
let links = [];

for (let i=0; i<h2Collection.length; i++) {

  links.push(`<a href="http://www.whateversite.com/${h2Collection[i].innerText.replace(/[^a-zA-Z]+/g, "-")}.html">${h2Collection[i].innerText}</a>`)

} // Next i: h2 element;

document.getElementById('output').innerHTML = links.join('<p>');
.blink {
  animation: blinker 1s step-start infinite;
  color: #ff0000;
  font-weight: bold;
  font-size: 0.85em;
}

@keyframes blinker {
  50% {
    opacity: 0;
  }
}
<div id="toc">
    <div id="contents">
      <h2>What is <span style="color:red">JS?</span></h2>
      <h2 style="margin-top:1em;">Python Jobs - <span class="blink">New!</span></h2>
      <h2 style="margin-top:1em;">
        What is Python?
      </h2>
    </div>

    <div id=output></div>
  </div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Sending data between controllers in AngularJS

As a newcomer to Angular, I am facing challenges in understanding how to utilize a Service to transfer data from one controller to another. My experience with Angular so far has involved successfully calling controllers and passing data to them from withi ...

Issues with the structure of React hooks arrays

I have an array containing questions retrieved from the database. Since I do not know how many questions are in the array, I have structured it with mapping functions on each element. <Fragment> <div className="paddingSection"> <h1 cl ...

I am curious about how to implement overflow:hidden along with position:sticky in React.js

My attempt to address the white space issue caused by a navbar I created led me to try using overflow:hidden. The navbar is generated using the Header component, and I desired for it to have a position: sticky attribute. However, I realized that I cannot ...

Navigating through a JSON file using the map function within a React application

As a newcomer to react, I am currently trying to grasp the concepts involved. Specifically, I am attempting to showcase a collection of email addresses and usernames from a json file structured like this: { "results":[ { "gender":"female" ...

React does not play well with the Sendgrid Node.js library

Seeking assistance with integrating node.js to handle email sending on my website. Interested in having the email form immediately send upon submission, rather than using the standard "mailto" action. Utilizing Sendgrid as the email service for API and ser ...

Using HTML and C# to Deliver Emails

I've encountered a challenge with my HTML page that includes a textbox for users to input their email. When the submit button is clicked, an email should be sent to a specific email address defined in the code, and a pop-up box indicating "Email Sent" ...

How can I display the value stored in draft.js in a different component using React?

I'm new to using React and struggling to figure out how to display the value from another component in the edit file component. I've successfully created a message using draft.js rich text editor and can save it to the backend database. Now, I ne ...

Tips for obtaining and storing multiple inputs within the same readline.question prompt in separate variables

Seeking to gather multiple user inputs in a single readline question and assign them to different variables? You're not alone! Ran into this challenge myself while trying to figure out the solution. Code import * as readline from 'node:readline&a ...

Parallel mapping with simultaneous multiple inserts

I am working on inserting a list of topics with unique slugs into a MongoDB database. Here are two example topics: { title: "my title" }, { title: "my title" } After generating the slugs, I need to insert the topics: // Insert each topic async.map( ...

Unable to assign an IP address to an Express JS application

Struggling to test a specific endpoint in Express, but consistently encountering a 404 error. var express = require("express") var app = express() //var http = require('http').Server(app) app.get('/', function(req,res){ res. ...

AJAX seems to be struggling to recognize JSON data as JSON format

I am facing an issue with my AJAX call where the data received from the server is not being treated as JSON, despite setting the datatype to json: function RetrieveMateriasFromServer(callback){ var status_aux; //HTTP request for data from the given UR ...

Which directives in AngularJS facilitate one-way binding?

Which directives in AngularJS support one-way binding? While ng-model enables two-way binding, what about ng-bind and {{ }} expressions - do they also support one-way binding? ...

Is there a way to compel @keyframes to continue playing until it reaches its conclusion even after a mouseout event

Whenever I hover over an element, a keyframe animation starts playing. However, when I move the cursor away, the animation stops abruptly. Is there a way to ensure that it plays until the end? I've tried using the animationend event, but it doesn&apos ...

The error message "ReferenceError: express is not defined" indicates that Node.js is

Recently, I started using Nodejs to develop web servers, utilizing the express module. To install it, I used the command: "sudo npm install -g express". However, upon running the program, an error occurred: "ReferenceError: express is not defined ...

personalized options for initiating and concluding html audio component

I am currently facing an issue with my html audio element that plays a track. The setup is quite straightforward: <audio controls loop="loop"> <source type="audio/wav" src="song.wav"> </audio> However, I need to create custom start ...

Locating numerous occurrences of a specific string within an array and rearranging them

I am facing an issue with organizing data stored in the 'names' variable within the code snippet. The task at hand involves converting a string into an array, rearranging the elements within the array to ensure the text is in the correct order. W ...

Is it possible to efficiently transfer a Tensorflow.js Tensor between Node.js processes?

Currently, I am in the process of building an AI model using Tensorflow.js and Node.js. One of the challenges I am facing is handling a large dataset in a streaming manner due to its size being too massive to be stored in memory all at once. This task invo ...

Prevent automatic scrolling by clicking when you reach the bottom

In order to replicate the issue I am experiencing, please click on the down button four times, and then click on the up button. You will observe that you need to click the up button one extra time for it to function properly. How can I detect when it has r ...

What is the best way to finish up the JavaScript code below?

Having just started learning JavaScript, I am struggling to figure out how to use JavaScript to clear the text in a text box and move it below the box when a user starts typing. I've been watching this tutorial http://codepen.io/ehermanson/pen/KwKWEv ...

JavaScript still displaying empty values despite correct syntax being used

Here is the HTML form I have created: <form accept-charset="utf-8" onsubmit="return validateForm()"> <input placeholder="Enter Username" type="text" id="user"> <input placeholder="Enter Your Password" type="password" id="pass"> ...