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++
}

What does the HTML look like?

Here it is:

Please post the full actual HTML code (not an image).

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>

First of all, you have an else keyword that is out of place. An if/else statement would look like:

if () {

}
else {

}

Instead, you have:

if () {

};
nextSibling = nextSibling.nextElementSibling;

else {

}

Is nextSibling = nextSibling.nextElementSibling; supposed to execute as part of the if condition or always execute regardless of the if condition? Or is it suppose to execute as part of the else code block?

Also, what do you think the following is attempting to select?

let current = document.querySelectorAll(".class h2, .class h3");

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++
}

The code you posted above has a syntax error (missing single quotation mark). Once you fix that, you should also notice the following error:

Uncaught InvalidCharacterError: Failed to execute ‘add’ on ‘DOMTokenList’: The token provided (‘collapse-[object Attr]’) contains HTML space characters, which are not valid in tokens.

Try fixing these first.

I apologize, corrected. ( missing quotation i mean)

You still have the 2nd error I mentioned above because of the following line:

nextSibling.classList.add("collapse-" + id_collapse);

id_collapse is a Node and not a string. You the need the Node’s value.

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).

or just id?

id_collapse = nextSibling.previousElementSibling.id;

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++
}

What is your goal with this regex?

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

What is the definition of a valid charater?