How to retrieve id from a heading and apply it as collapse class to non-headings

So I’m unable to retrieve the id from the headings I’m getting null value here:

id_collapse = nextSibling.previousElementSibling.getAttributeNode("id");
(full code at the bottom)
have tried using getAttributeNode as well as getAttribute and using [0] but is not working…

If I use console.log I only get [object HTMLHeadingElement]
So how can I further debug this? I would like to learn this as well.

Code (simplified version):

let counter = 0;

let current = document.querySelectorAll('.class h2, .class h3');
while(current[counter]) {
  
  let nextSibling = current[counter].nextElementSibling;

  while (nextSibling) {  
      
      if((nextSibling.previousElementSibling.nodeName == "H2") || (nextSibling.previousElementSibling.nodeName == "H3")) {
         id_collapse = nextSibling.previousElementSibling.getAttributeNode("id");                 
         // just for testing
         nextSibling.previousElementSibling.style.color = "green";    
         // id_collapse is always null
         nextSibling.classList.add("collapse");
         nextSibling.classList.add("collapse-" + id_collapse);
      };

      nextSibling = nextSibling.nextElementSibling;

    else {
      break;
    }
  }
  counter++
}

Here it is:

Sure okey, this is extract actually, let me know if it is ok:

<div class="post-content descripcion-seccion">
            <h2 style="color: green;" id="introducción:-¿qué-es-css?" class="anchor-heading">Introducción: ¿Qué es CSS?<a class="anchor-link" href="#introducción:-¿qué-es-css?">#</a><a data-bs-toggle="collapse" role="button" data-bs-target=".collapse-introducción:-¿qué-es-css?" aria-controls="collapse-introducción:-¿qué-es-css?" aria-expanded="true" href="#">&gt;</a></h2>

            <p style="margin-left: 3%; margin-right: -3%;" class="collapse collapse-null">Here it goes text.</p>

            <h2 style="color: green;" id="sintaxis-general" class="anchor-heading">Sintaxis General<a class="anchor-link" href="#sintaxis-general">#</a><a data-bs-toggle="collapse" role="button" data-bs-target=".collapse-sintaxis-general" aria-controls="collapse-sintaxis-general" aria-expanded="true" href="#">&gt;</a></h2>

            <p style="margin-left: 3%; margin-right: -3%;" class="collapse collapse-null"><a href="https://aprende-web.net/css/css1_1.php">https://aprende-web.net/css/css1_1.php</a></p>
</div>

Thank you. Most likely when I simplified the code I got it wrong, posting the full JS and also checking.

let counter = 0;

let current = document.querySelectorAll('.post-content h2, .post-content h3');
while(current[counter]) {
  
  let nivel = current[counter].tagName.slice(-1)
  nivel -= 1;
  let calculoPadding = nivel * 4;

  if (calculoPadding == 4) {calculoPadding = 3}
  else if(calculoPadding == 8) {calculoPadding = 9.5}
  let nextSibling = current[counter].nextElementSibling;

  while (nextSibling) {  
    if(nextSibling.nodeName == "P" || nextSibling.nodeName == "UL" || nextSibling.nodeName == "PRE"){

      
      nextSibling.style.marginLeft = calculoPadding + "%"; 
      nextSibling.style.marginRight = -1 * calculoPadding + "%";


      if((nextSibling.previousElementSibling.nodeName == "H2") || (nextSibling.previousElementSibling.nodeName == "H3")) {

         id_collapse = nextSibling.previousElementSibling.getAttributeNode("id");
         //id_collapse = document.getElementById("sintaxis-general").getAttribute("id");         
         nextSibling.previousElementSibling.style.color = "green";     
         
         nextSibling.classList.add("collapse");
         nextSibling.classList.add("collapse-" + id_collapse);
      };


      nextSibling = nextSibling.nextElementSibling;


    }
    else {
      break;
    }
  }
  counter++
}

I apologize, corrected. ( missing quotation i mean)

Yes. Thank you! I’m on it. I think that’s caused by another code that I know I should fix.
I’m posting it now give me a sec.

You should probably log out id_collapse (and declare the variable as well). I don’t think you want what it returns but just the id string value (so .value of what getAttributeNode returns, or just getAttribute instead).

Sure, but as they are using the methods that is what I posted.

I do think with a lot of code, sometimes getAttribute can read a little better (for scanning the code). Although I rarely use it, to be honest.

In any case, they need to fix the looping.

Thank you both!

getAttribute it is then, simpler.

Thats a combination I forgot to mention I tried, said that is a good one too IMHO simpler.

I think the error is in the regex part on the ID creation, but I’m not expert on regex… so I planned to fix that later.

window.onload = function () {

            //add anchor link to all headings
            document.querySelectorAll('.post-content h1, .post-content h2, .post-content h3, .post-content h4, .post-content h5, .post-content h6').forEach($heading => {

                //create id from heading text
                var id = $heading.getAttribute("id") || $heading.innerText.toLowerCase().replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '').replace(/ +/g, '-');

                $heading.setAttribute('id', id);
                $heading.classList.add('anchor-heading');

                //create anchor
                $anchor = document.createElement('a');
                $anchor.className = 'anchor-link';
                $anchor.href = '#' + id;
                $anchor.innerText = '#';

                //append anchor link after heading text
                $heading.appendChild($anchor);

Updated code for the above apart. added declaration of id_collapse and switched to getAttribute

let counter = 0;

let current = document.querySelectorAll('.post-content h2, .post-content h3, .post-content h4, .post-content h5, .post-content h6');
while(current[counter]) {

  let nivel = current[counter].tagName.slice(-1)
  nivel -= 1;
  let calculoPadding = nivel * 4;

  if (calculoPadding == 4) {calculoPadding = 3}
  else if(calculoPadding == 8) {calculoPadding = 9.5}
  let nextSibling = current[counter].nextElementSibling;
  let id_collapse;

  while (nextSibling) {  
    if(nextSibling.nodeName == "P" || nextSibling.nodeName == "UL" || nextSibling.nodeName == "PRE"){

      
      nextSibling.style.marginLeft = calculoPadding + "%"; 
      nextSibling.style.marginRight = -1 * calculoPadding + "%";


      if((nextSibling.previousElementSibling.nodeName == "H2") || (nextSibling.previousElementSibling.nodeName == "H3")) {

         id_collapse = nextSibling.previousElementSibling.getAttribute("id");
                 
         nextSibling.previousElementSibling.style.color = "green";     
         
         nextSibling.classList.add("collapse");
         nextSibling.classList.add("collapse-" + id_collapse);
      };


      nextSibling = nextSibling.nextElementSibling;


    }
    else {
      break;
    }
  }
  counter++
}

I think it was removing invalid characters, I’m searching for the original source…

special characters** are invalid If IRC

More like the first but I think here’s the correct answer, hence the regex I should had used.

Will try that and report back.
Update: I will go removing anything that could potentially be problematic

The goal should be to generate valid id values and removing anything that could potentially be problematic. The above regex accepted answer doesn’t seem to work neither.
var id = $heading.getAttribute("id") || $heading.innerText.toLowerCase().replace(/^[^a-z]+|[^\w:.-]+/g, '').replace(/ +/g, '-');

Question: even for a simple name or generated id it is not generating the collapse-class… so may the problem be somewhere else as well?

This generates the ids for each header which are then used to generate the classes for every sibling that is not a header

Would it do if I rather just publish this on my website? it is meant to be a CSS guide…

Thank to everyone. I figured out, I had to wrap both codes inside window.onload function for it to properly load.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.