1

Context: I'm lazy, and I'm trying to dynamically/automatically create menu buttons which are hyperlinked to the headers of a page with raw JavaScript.

My site loads the content of the body from an external file located at the same folder with document.onload, and I'm trying to set up a menu function, which then should load the default page's menu items. Loading the menu manually each time I change from one page to another works, as I've included loadMenues(thispage) on the end loadContents(), but it doesn't work as soon as the page is loaded, as loading the body content does. I don't understand this behaviour.

function setVisible(thisdiv){
    var alldivs = document.getElementsByClassName("container");
    [].forEach.call(alldivs, function(uniquediv){
        document.getElementById(uniquediv.id).style.display = "none";
        return;
    });
    document.getElementById(thisdiv).style.display = "block";
    window.scrollTo(0,0);
    loadMenues(thisdiv);
}
window.onload = function(){
    loadContent("personalinfo");
    loadContent("contactdetails");
    setVisible("personalinfo");
    loadMenues("personalinfo");
}

I'm explaining this, secondary question, in order to contextualize my main problem.

loadContents(file) is a function which extracts the contents from the requested file. The layout of each of these files is the same, pretty much, with each section of the file being separated by a custompadding div, where its first child is a h1 element as shown below:

<html>
    <div class="custompadding">
        <h1 id="headerpersonaldetails">Personal details</h1>
        <p>Lorem ipsum</p>
    </div>
    <div class="custompadding">
        <h1 id="headercontactdetails">Contact details</h1>
        <p>Lorem ipsum</p>
    </div>
</html>

I'm trying to set up a menu item for each of these headings, which scrolls to the clicked-on header. Setting up each menu-item manually works as expected, but I want to automatize it, so changing any file will automatically add the menu items to whichever page we change to. Following is my code which adds these elements to the divisor, but I'm having issues handling the onclick function.

function loadMenues(file) {
    var rightmenu = document.getElementById("right-menu");
    while(rightmenu.firstChild){
        rightmenu.removeChild(rightmenu.firstChild);
    }
    [].forEach.call(document.getElementById(file).children, function(custompaddingchild) {
    console.log(custompaddingchild);
    headerelement = custompaddingchild.getElementsByTagName("h1")[0]
    newbutton = document.createElement("div");
    newbutton.setAttribute("class", "menu-item");
    let movehere = function() { location.href="#"+headerelement.id; console.log(headerelement.id); }
    newbutton.onclick = movehere;
    /*rightmenu = document.getElementById("right-menu");*/
    buttonspanner = document.createElement("span");
    buttoncontent = document.createTextNode(headerelement.innerHTML);
    buttonspanner.appendChild(buttoncontent);
    newbutton.appendChild(buttonspanner);
    rightmenu.appendChild(newbutton);
    });
}

The first part of the function deletes all the nodes which already are in the menu, in order to add the new ones when changing pages.

Trying to define newbutton.setAttribute() with onclick results in a SyntaxError (fields are not currently supported) in Firefox. It doesn't work if I set a static string as newbutton.setAttribute("onclick", "location.href=#headerpersonalinfo"); either.

Trying to set a static anchor link with newbutton.onclick set to a function, instead, works, such that

newbutton.onclick = function() {
    location.href = "#headerpersonalinfo";
}

and this is pretty much how my current code is set up, except that I have given this function a unique variable, which I then call.

The problem I have is this, as I see it: The variable is redefined each time it finds a new header, so calling the function sends the user to the last header, and not the expected one. How can I set the function to be parsed at the moment I define onclick with it, and not call the function when the user presses the button?

PS: I'm using my own internal naming convention of files, headers, and items, in order to modularize my site as much as I can. Since this is a website only intended for my Curriculum Vitae, I'm its only developer.

7
  • 2
    Code in the form of images are discouraged. In fact, Code must not be in the form of image while posting a question here on Stack Overflow Commented Feb 24, 2019 at 15:35
  • Would you mind being a little clearer about exactly what should happen in your codesandbox, that isn't happening? Commented Feb 24, 2019 at 15:51
  • Pressing any of the buttons on the right menu should lead to the header of the same name. It, instead, leads to the last header element. @TomMettam Commented Feb 24, 2019 at 15:53
  • Have you forgotten to include contactdetails.html? Commented Feb 24, 2019 at 15:57
  • I intentionally left it out as it is unnecessary for the problem I'm having. @TomMettam Commented Feb 24, 2019 at 15:58

1 Answer 1

1

The issue occurs because the you are hoisting "variables" to the global scope (newbutton and headerelement).

Set them to block scoped variables (const or let) and you will see that it works: https://codesandbox.io/s/rm4ko35vnm

function loadMenues(file) {
  var rightmenu = document.getElementById("right-menu");
  while (rightmenu.firstChild) {
    rightmenu.removeChild(rightmenu.firstChild);
  }
  [].forEach.call(document.getElementById(file).children, function(
    custompaddingchild
  ) {
    console.log(custompaddingchild);
    const headerelement = custompaddingchild.getElementsByTagName("h1")[0];
    console.log(headerelement.innerHTML);
    const newbutton = document.createElement("div");
    newbutton.setAttribute("class", "menu-item");
    console.log(headerelement.id);
    let movehere = function() {
      location.href = "#" + headerelement.id;
      console.log(headerelement.id);
    };
    newbutton.addEventListener('click', movehere);
    const rightmenu = document.getElementById("right-menu");
    const buttonspanner = document.createElement("span");
    buttoncontent = document.createTextNode(headerelement.innerHTML);
    buttonspanner.appendChild(buttoncontent);
    newbutton.appendChild(buttonspanner);
    rightmenu.appendChild(newbutton);
  });
}
Sign up to request clarification or add additional context in comments.

1 Comment

Setting elements to const solved the problem. Having two elements with the same id breaks it for both div, so be careful.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.