Search code examples
javascripthtmlfont-family

How to iterate through all elements of <tag> and change their font?


I'm trying to implement a font changer for my website with JavaScript and , but the header iteration part won't work.

undefined is not an object (evaluating 'tagElement.style.fontFamily = fontsObj.headerFont')

Here's my code

HTML:

<select name="font-select" id="font-select">
  <option value='{"headerFont":"system-ui, -apple-system","bodyFont":"system-ui, -apple-system"}'>System & System</option>
  <option value='{"headerFont":"Lato","bodyFont":"Karla"}' selected="selected">Lato & Karla</option>
  <option value='{"headerFont":"Lora","bodyFont":"Lato"}'>Lora & Lato</option>
  <option value='{"headerFont":"Philosopher","bodyFont":"Mulish"}'>Philosopher & Muli(sh)</option>
</select>

JS:

function changeFontStyle(fonts) {
    const headerTags = ["h1", "h2", "h3", "h4", "h5", "h6"];
    var bodyText;
    const fontsObj = JSON.parse(fonts.value);
    
    // Update headers' font
    for (let headerTag in headerTags) {
        var tagElements = document.getElementsByTagName(headerTag);
        for (let tagElement in tagElements) {
            tagElement.style.fontFamily = fontsObj.headerFont;
        }
    }
    
    // Update body font
    bodyText = document.getElementsByTagName("html")[0];
    bodyText.style.fontFamily = fontsObj.bodyFont;
}

var fontSelector = document.getElementById("font-select")
fontSelector.onchange = changeFontStyle(fontSelector);

The JSON.parse part seems to work right (as opposed to comments below this answer), at least I can't see any parse errors.

Edit: Newest version of question here


Solution

  • To keep the solution performant we'll use querySelectorAll instead of getElementsByTagName to select all the H tags in one go and loop over them only once to set the new font. Also, as querySelectorAll return a NodeList, we will be able to loop through the returned elements using the forEach method instead of having nested for..in loop.

    The idea is simple:

    • listen for the dropdown change
    • select all the H elements using querySelectorAll
    • loop through the returned elements and change their font
    • then change the body's font

    Here's a live demo:

    const fontsDropdown = document.getElementById('font-select'),
      headerTags = ["h1", "h2", "h3", "h4", "h5", "h6"],
      fontsChanger = () => {
        const fontsJson = JSON.parse(fontsDropdown.value);
    
        /** we'll select all the elemnts based on the selector found in the "headerTags" array by concatinating those selector with an "," (we'll have: "h1,h2,h3,h4,h5,h6") */
        document.querySelectorAll(headerTags.join(',')).forEach(h => h.style.fontFamily = fontsJson.headerFont);
    
        /** change the body's font also */
        document.body.style.fontFamily = fontsJson.bodyFont;
      };
    
    /** run on page load so the font change based on the initial selected value in the dropdown */
    fontsChanger();
    
    /** listen for the "change" event on the fonts dropdown and call "fontsChanger" function when a change occurs */
    fontsDropdown.addEventListener('change', fontsChanger);
    <p>Am a P tag</p>
    <h1>Am an H1 tag</h1>
    <h3>Am an H3 tag</h3>
    <h6>Am an H6 tag</h6>
    
    <select name="font-select" id="font-select">
      <option value='{"headerFont":"system-ui, -apple-system","bodyFont":"system-ui, -apple-system"}'>System &amp; System</option>
      <option value='{"headerFont":"Lato","bodyFont":"Karla"}' selected>Lato &amp; Karla</option>
      <option value='{"headerFont":"Lora","bodyFont":"Lato"}'>Lora &amp; Lato</option>
      <option value='{"headerFont":"Philosopher","bodyFont":"Mulish"}'>Philosopher &amp; Muli(sh)</option>
    </select>

    The above demo can still be improved further, especially if you're sure that you won't deal H tags that are added dynamically.

    Sidenote: not all the devices out there have all the fonts you have specified in the dropdown. It is likely that most of the devices do not have some fonts installed so the change won't occur in that case.

    Another sidenote: changing the H tags font is useless as they will inherit that change from the body. Anyway, I kept your logic in the above demo unchanged.