Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Comments on Change font-family with JavaScript
Parent
Change font-family with JavaScript
I want to change the font-family
of all elements in a document with JavaScript.
I have tried this:
document.querySelectorAll("body").forEach( (e)=>{
e.style.fontFamily = "arial";
});
This changed the font-family of generally everything besides what already had a font-family
defined in CSS ID or class.
How could I make the JavaScript command to "run through" these IDs and classes?
Post
The following users marked this post as Works for me:
User | Comment | Date |
---|---|---|
user36363 | (no comment) | Nov 12, 2021 at 18:14 |
To do that, you could change the selector from body
to *
, as the other answer said. By selecting only body
, it won't change child elements that has defined a more specific rule, and that's why you need to set the style for all of them.
But there are some corner cases that I think it's worth exploring.
Not everything needs to be selected
Although document.querySelectorAll("*")
works, I think it's overkill. By selecting everything, you'll be changing the style of elements that don't need to, such as head
, title
, meta
, script
, and many others for which changing the font is pointless (for example, title
is showed only in the browser tab and you can't change its style, meta
doesn't have rendered content and there's no point in changing its font, etc).
Of course setting the font family for those elements doesn't cause errors, but it has no effects at all. And why doing something that will have no effect?
Instead, you could limit the selection only to elements inside the document's body:
// select descendants of body instead of the whole document
document.body.querySelectorAll("*").forEach((e) => {
e.style.fontFamily = "Arial";
});
This eliminates most elements that don't need to be styled (such as "everything inside head
"). That will still select "non-text" elements inside body
(such as script
), though.
And of course this excludes body
itself, which can be included with a little extra code:
// change style of body and its descendants
for (const e of [ document.body, ...document.body.querySelectorAll("*") ]) {
e.style.fontFamily = "Arial";
}
I basically created an array containing body
and its descendants, and I also changed from forEach
to for..of
, just to show another way of looping through the elements.
Some things might break
Maybe it's not your case, but many sites today use things like Font Awesome, which provides a "font" with glyphs that are like images. Codidact uses it in many places, such as the icons on the top bar:
Just in case the site's layout change in the future, here's an image of the current icons (in November 2021):
The HTML for this is:
<i class="fas fa-fw fa-inbox" title="Notifications"></i>
<i class="fas fa-fw fa-mobile-alt" aria-label="Mobile Sign In" title="Mobile Sign In"></i>
And all the icons in the bar are similar. If you check them in browser's console, you'll see that the element's font-family is "Font Awesome 5 Free". If I change it to "Arial", the icons are not displayed anymore:
That's because Font Awesome uses code points in the Private Use Area, for which many fonts don't have the respective glyphs.
So inside the loop you must also check if there are elements that you don't want to change (the criteria will vary on a case by case basis). Or you can use a more complex selector to exclude what you don't want (such as document.body.querySelectorAll("*:not(i)")
to exclude i
tags).
Obviously, if your page doesn't have such cases and changing the font for all elements is fine, using *
will do the job.
Pseudo-elements
The solution above doesn't work for CSS pseudo-elements, such as ::first-letter
, ::after
, ::before
, etc. For example, given this HTML:
<p>Abc</p>
And this CSS:
p {
font-family: monospace;
font-size: 50px;
}
p:first-letter {
font-family: Times;
}
p:after {
font-family: Times;
content: "xyz";
}
The letter "A" and the text "xyz" will be displayed with "Times" font, while "bc" will have a monospaced font.
After running the JavaScript code above, only "bc" will be changed to Arial:
That's because querySelectorAll
doesn't select nor return pseudo-elements. So ::first-letter
and ::after
remain with their original fonts.
In that case, you can use getComputedStyle
to check for pseudo-elements styles, and change them accordingly. Unfortunately, getComputedStyle
returns a read-only object, so we can't change it directly. One solution is to add a style directly in the document, by using insertRule
:
// include here all pseudo elements you want to check
const pseudoElements = ['first-letter', 'after', 'before'];
for (const e of [ document.body, ...document.body.querySelectorAll("*") ]) {
e.style.fontFamily = "Arial";
// check pseudo elements
for (const psEl of pseudoElements) {
var style = window.getComputedStyle(e, `::${psEl}`);
if (style.fontFamily !== 'Arial') { // if font family is not Arial, change it
// computed style is read-only, so we must change it like this
const sheet = document.styleSheets[0];
// insert rule at the end, so it overwrites any others
sheet.insertRule(`${e.tagName.toLowerCase()}::${psEl} { font-family: Arial }`, sheet.cssRules.length);
}
}
}
With that, all elements and pseudo-elements are updated to Arial:
Again: if you don't have any pseudo-elements that need to be changed, all this work is not needed. I just wanted to cover more possible cases, because "changing all elements" is not always that simple.
0 comment threads