Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs

Dashboard
Notifications
Mark all as read
Q&A

Change font-family with JavaScript

+2
−1

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?

Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

2 answers

+6
−0

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

Icons for Inbox and Mobile Sign In, as they were 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:

Top bar with icons not displayed after changing font family to Arial

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.

First letter and "xyz" with Times, paragraph with monospace

After running the JavaScript code above, only "bc" will be changed to Arial:

Paragraph set to Arial, but pseudo elements didn't change

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:

All text in 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.

Why does this post require moderator attention?
You might want to add some details to your flag.

1 comment thread

I especially like: ```js // select descendants of body instead of the whole document document.bo... (2 comments)
+2
−0

Without knowing your use case, the simplest modification would just be to target everything:

document.querySelectorAll("*").forEach((e) => {
    e.style.fontFamily = "arial";
});

(As an aside, if you want to target the body element, just use document.body. No need for selectors.)

Why does this post require moderator attention?
You might want to add some details to your flag.

1 comment thread

I thank you for the sharp distinction (2 comments)

Sign up to answer this question »