2

I'm working on a simple tool for designers where the user would be able to input some text and then generate through various fonts to find the best choice for their project.

I'm struggling with finding the best way to load in the fonts. Obviously, I don't really want to load in all the fonts at once, so I need to load in the font only once it has been generated. Using the @font-face CSS rule would be ideal, but I can't seem to figure out how to change the src of the font with Javascript.

Right now, my method is to use Google Fonts, where you load the font with a css link like <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">. Then I can use Javascript to change the href of that link, which loads in the new font. But this method limits me to using only Google Fonts, whereas it would be ideal to be able to load in local web fonts, TypeKit fonts, etc.

Does anyone have any suggestions for the best way to load in the fonts or how to use Javascript to access the @font-face rule in CSS?

Luke Johnson
  • 183
  • 2
  • 12

2 Answers2

3

Set up a stylesheet that controls the font, with an id that lets you remove it and rebuild it at will. For example:

function setFontTo(fontName) {
  const styleId = 'font-style-sheet';

  // Get a reference to the current in-use stylesheet, if there is one.
  const fontStyleSheet = document.getElementById(styleId);

  // Then define a new stylesheet with an updated @font-face rule:
  const newFontStyleSheet = document.createElement("style");
  newFontStyleSheet.id = styleId;
  newFontStyleSheet.textContent = `
    @font-face {
      font-family: 'main-dynamic-font';
      src: url(assets/fonts/${fontName}.woff) format('woff');
    }
  `;

  // Then we swap: add the new rule first, then remove the old one.
  // That way you don't get a flash of unstyled text.
  document.head.appendChild(newFontStyleSheet);

  if (fontStyleSheet) {
  document.head.appendChild(newFontStyleSheet);
    fontStyleSheet.parent.removeChild(fontStyleSheet);
  }
}

And then you make sure to, in your CSS file/bundle, define a class that uses that font, so something like:

.main-content {
  font-family: 'main-dynamic-font', serif;
}

And then in your markup you use that class for any element that requires styling text using the user-selected font:

<div class="main-content blah blah ...">
  ...
</div>

Finally, you make sure your font selector lets users pick font names that, as value, map to the actual font filenames (and you make sure to always use woff):

<select id="font-picker">
  <option value="roboto-regular">Roboto (regular)</option>
  ...
</select>

with a js handler for that selector.

fontSelector = document.getElementById("font-picker");
fontSelector.addEventListener("change", evt => {
  // read the selected value, and then call the font swap function here.
});
Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
0

This Demo interacts with the user with some radio buttons. Each radio button corresponds with a font family from Google Fonts. The parts that are commented out deal with localStorage (no point with altering styles if you lose them when leaving the page). It won't function here on SO, but it will save your change in a less strict environment (normal circumstances).

References

HTMLFormControlsCollection

Event Delegation

CSS Variables

Template Literals


Demo

//document.onDOMContentLoaded = init;

var form = document.forms.font;

form.addEventListener('change', fontInput);

function fontInput(e) {
  var fc = form.elements;
  var fam = ['Montserrat', 'Roboto', 'Open Sans', 'Raleway', 'Quicksand'];
  var idx = parseInt(fc.font.value, 10);
  var family = `${fam[idx]}`;
  var url = `https://fonts.googleapis.com/css?family=`;
  var fontUrl;
  if (e.target.name === 'font') {
    var famFont = family.replace(/\s/g, `+`);
    fontUrl = `${url}${famFont}`;
    document.getElementById('link').href = fontUrl;
    console.log(fontUrl);
    //localStorage.setItem('fontUrl', fontUrl);
  }
  cssVAR(family);
  e.stopPropagation();
}

function cssVAR(str) {
  var root = document.documentElement
  root.style.setProperty('--fam', str);
  return false;
}

/*
function init(e) {
  var saved = localStorage.getItem('fontUrl');
  if (!saved) {
    localStorage.setItem('fontUrl', 'https://fonts.googleapis.com/css?family=Open+Sans');
    document.getElementById('link').href = 'https://fonts.googleapis.com/css?family=Open+Sans';
  } else {
    document.getElementById('link').href = saved;
  }
}
*/
:root {
  --fam: "Raleway";
}

.as-console-wrapper {
  width: 40%;
  margin-left: 60%;
  max-height: 60px;
}

.as-console-row:after {
  display: none;
}

body {
  font-family: var(--fam);
}

#font {
  font-family: Consolas;
  font-size: 13px;
}
<link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' id='link'>
<form id='font'>
  <fieldset>
    <legend>Font-Family</legend>
    <label>Montserrat</label>
    <input type='radio' value='0' name='font'>
    <label>Roboto</label>
    <input type='radio' value='1' name='font'>
    <label>Open Sans</label>
    <input type='radio' value='2' name='font'>
    <label>Raleway</label>
    <input type='radio' value='3' name='font' checked>
    <label>Quicksand</label>
    <input type='radio' value='4' name='font'>
  </fieldset>
</form>
<h1>Dynamic CSS</h1>
<p><q>The face of the moon was in shadow.</q></p>
<cite>Mr. Spaceship, by Philip K. Dick</cite>
zer00ne
  • 41,936
  • 6
  • 41
  • 68