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
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:
H
elements using querySelectorAll
body
's fontHere'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 & System</option>
<option value='{"headerFont":"Lato","bodyFont":"Karla"}' selected>Lato & Karla</option>
<option value='{"headerFont":"Lora","bodyFont":"Lato"}'>Lora & Lato</option>
<option value='{"headerFont":"Philosopher","bodyFont":"Mulish"}'>Philosopher & 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 thebody
. Anyway, I kept your logic in the above demo unchanged.