How to prevent material icon text from showing up when Google's JS fails to convert them?
Asked Answered
T

17

47

How do you prevent material icon text from showing up when Google's JS fails to convert them to icons?

Icons are defined in markup as such:

<span class="material-icons">icon_name</span>

Example: https://archive.fo/CKqKG/scr.png (see the top row of buttons).

Material Icons Documentation: https://material.io/icons/

This is also an issue in Google search where Google will actually read and save the div's text instead of ignoring it.

Example: https://i.sstatic.net/dKH1e.png

I understand that one solution is to simply switch to .PNGs (supplied by Google). I'd like to do whatever results in less (network) load on the user's system.

Thanks!

Tony answered 18/1, 2017 at 3:21 Comment(1)
S
19

you can use font-display: block;, just add this CSS to your HTML head:

<style>
   @font-face {
      font-family: 'Material Icons';
      font-display: block;
    }
</style>

for more information font-display

Sharpeared answered 14/11, 2018 at 13:45 Comment(5)
This will only work if you specify the src of the font in @font-face, and not use @import or a link tag in your head. Unfortunately, this is not (yet) possible with Google Fonts: css-tricks.com/…Oak
Note: font-display: block will display an invisible font-face, which means that while the font is loading, an invisible text would be displayed which would have the same length as the fallback text. This ligature text is usually wider than the actual font, so you would need to set some styles so that even though it is invisible due to font-display:block, the width is still less so there is no flicker.Demit
Old question, but new-to-me issue. You can now specify the font-display property with Google fonts, so fonts.googleapis.com/… works.Truelove
I tried your solution and it's working, but not when network speed is too slow. To be more clear, it will show block (actually nothing) instead of texts, for about two seconds, so it's fine when I try fast 3G mode and it will not show texts to the user, but for slow 3G mode, the problem is still there, just with about two seconds delay! Is there any way to increase this delay?!Absorbed
@Absorbed I can +1 this it is still a major issue.Twobyfour
S
13

I've been struggling with a similar situation: my problem was not that the icons never loaded, just that they could take a while to load on slower connections and until they loaded ugly, unformatted text like sentiment_very_satisfied would be shown on the page (often many times larger than the surrounding text as well making it very obvious).

The other solutions here didn't work for me (including font-display:block which I thought might be promising), so I came up with my own using CSS and jQuery. I'm sure you could easily adapt it to use vanilla JS.

CSS:

.material-icons{
    opacity:0;
}

jQuery:

$(window).load(function() {
    $('.material-icons').css('opacity','1');
});

The trick here is that, unlike the more commonly used $(document).ready() listener, $(window).load() waits for all elements of a page to be downloaded before being triggered. In this case, that means it won't change the opacity of the icons until the icon font has been downloaded.

The downside is that the icons won't show up until everything on the page has been downloaded, but that was a trade-off I was willing to make to avoid having huge spans of text visible on my page before the icon font loaded.

(I also added a transition to the CSS .material-icons{transition:opacity 0.5s;} so they showed up nice and smooth.)

Sedberry answered 30/1, 2019 at 21:49 Comment(2)
I just checked and sentiment_very_satisfied is a real icon: material.io/resources/icons/…. Yeah, that's what I want flashing my users.Dunc
This, unfortunately, does not work in react. The querySelectorAll result is always empty in index.html of my project. Where do I put this code in the react project?Twobyfour
Y
8

In your "index.html", you should have the link to Google Material Icons. In this link, you have to include: "&display=block" at the end. I show you how it's the result:

  <link
  rel="stylesheet"
  href="https://fonts.googleapis.com/icon? 
  family=Material+Icons&display=block"
  crossorigin
  />

If you go now to the link, you will see that the document includes: "font-display: block;" (you have changed the "display").

And that's all. The name of the icon will not see more and the icon will charge correctly!

Yore answered 11/11, 2022 at 9:4 Comment(1)
Thats the only one that worked for me! It should be the 'right answer'! Thanks!Laveralavergne
S
6

If you are using Typekit's webfont loader, you can apply conditional classes to hide the icons while the web font is loading or if it failed to load, e.g.:

.wf-loading, .wf-materialicons-n4-inactive {
  .material-icons {
    display: none;
  }
}

You can of course apply other styling techniques according to your preferences for best results, e.g. font-size: 0;, it will depend on your site and use case.

To load the material icons with the webfont loader, use configuration like so:

window.WebFontConfig = {
  google: {
    families: [
      'Material Icons',
    ],
  },
};
Shatterproof answered 4/11, 2017 at 20:15 Comment(2)
can you kindly specify why the material icon fails to display and why it displays as text? and what could be the workaround?Fairground
There can be many reasons for material icons to fail to display, for example internet connection failure or otherwise a failed request to fetch the font files. In that case, if you use material icons with ligatures, when your code is something like <i class="material-icons">face</i>, the word "face" will be visible as plain text on your page. The work around in my answer prevents this text from showing up.Shatterproof
B
3

I am facing this same issue. I believe, though, that using a pseudo selector like i.material-icons:before can help. See this for more info.

---- EDIT : Working Example

i.material-icons:before{display:none;}

Berstine answered 29/8, 2017 at 21:19 Comment(0)
C
3

The right solution to this will be to add a max width of the same font-size and set overflow to hidden.

.material-icons {
    max-width: 16px;
    overflow: hidden;
}
Cattail answered 12/4, 2020 at 20:12 Comment(1)
It reduce the size of text but not hide the textSivia
F
3

I used to have the same problem (TOC), until I discovered that IcoMoon helps you create a css file with a class name identifier for each material icon you choose and also creates you the icon as a symbol SVG.

Let's see this with an example.

Google Fonts way (FOUC)

<span class="material-icons">
search
</span>

This code displays the search word while the icon font loads. However, with some good use of caching, preconnection, preload, and the display block, hopefully no one will pay attention to it.


IcoMoon way (Using Fonts)

<span class="icon-search"></span>

Here you can notice that there is no FOUC possibility because there is no default text to display.

.icon-search:before {
  content: "\e900";
}

With this extra class it will reserve an empty space until the source is loaded.


IcoMoon way (Using SVG symbol)

<symbol id="icon-search" viewBox="0 0 24 24">
<path d="M9.516 14.016q1.875 0 3.188-1.313t1.313-3.188-1.313-3.188-3.188-1.313-3.188 1.313-1.313 3.188 1.313 3.188 3.188 1.313zM15.516 14.016l4.969 4.969-1.5 1.5-4.969-4.969v-0.797l-0.281-0.281q-1.781 1.547-4.219 1.547-2.719 0-4.617-1.875t-1.898-4.594 1.898-4.617 4.617-1.898 4.594 1.898 1.875 4.617q0 0.984-0.469 2.227t-1.078 1.992l0.281 0.281h0.797z"></path>
</symbol>

<svg class="icon icon-search"><use xlink:href="#icon-search"></use></svg>

With this technique there is no need to wait for the fonts to load. BUT, if you have a large set of icons, the HTML size will be larger and maybe less maintainable.


Finally, if you're worried about the load time of your projects, you'll be glad to know that with this tool (IcoMoon) you only load the icons you use (OMG, this looks like an infomercial 🥴). Anyway, here are the steps to follow:

  1. Go to https://icomoon.io/app/#/select/library and select Material icons with the + Add button.
  2. Now select those icons you are going to use.
  3. Finally, select at the bottom the option Generate Font or Generate SVG.

You will have a zip file with css and fonts you can include in your project or the SVG paths to include in your HTML.

Some extra references to read:

Ferry answered 16/2, 2022 at 15:40 Comment(3)
Thanks for sharing this. Very handy to build a new font out of Material Icons (save a lot of 160kB in my case). A pity you can't change the weight and grade with Icomoon.Hypopituitarism
I am glad of hear that. What you mean with weight and grade? Maybe I could help.Ferry
Weight and grade are new features in the icon font, to change stroke weight and emphasises on the outlines. See the parameters on the right: fonts.google.com/iconsHypopituitarism
C
2

In case if your are using angularjs, fix might be

<i class="material-icons-outlined" ng-bind-html=" 'icon_text' "></i>

or if you are using jquery, then

HTML

<i material-icons="icon_text" class="material-icons"></i>

jQuery.onLoad

$('[material-icons]').each(function(){
  var icon_text = $(this).attr('material-icons');
  $(this).html(icon_text)
});

Icon appears after the page has loaded.

Crucifixion answered 2/7, 2019 at 6:27 Comment(2)
this looks like a promising answerEngine
it is not working. somehow same icon text is appeared on slow conncectionsCroy
B
2

instead of using material icons full text you can use their corresponding hex codepoints. of course this is not hiding but if the font is not loaded it just shows the unknown char symbol.

example:

<i class="material-icons">&#xE87D;</i>

you can find the list of codepoints at:

https://github.com/google/material-design-icons/issues/813#issuecomment-401601344

or

https://raw.githubusercontent.com/flutter/flutter/master/packages/flutter/lib/src/material/icons.dart

Beefwood answered 26/11, 2020 at 20:51 Comment(0)
J
1

Not a fix but on browsers that support preconnect you can try and load the fonts as soon as possible. Should help reduce the amount of time between text and icon being shown on slow connections.

<link rel="preconnect" href="//fonts.googleapis.com">
<link rel="preconnect" href="//fonts.gstatic.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
Jacinto answered 16/5, 2019 at 13:13 Comment(1)
One of the very few things you could actually do. Surely not a fix, but it's still something. Unfortunately the issue is that the request returns stylesheet, that is next adding a request for the font itself. Which is most of the times at the end of the queue. Still something though.Firestone
K
1

I found that it helps if you insert - &display=swap at the end of the href in the link tag like so:

<link
    href="https://fonts.googleapis.com/icon?family=Material+Icons&display=swap"
    rel="stylesheet"
  >

here is a link for reference

Kuvasz answered 15/2, 2021 at 17:39 Comment(1)
"The easiest way to avoid showing invisible text while custom fonts load is to temporarily show a system font (...) swap tells the browser that text using the font should be displayed immediately using a system font. Once the custom font is ready, it replaces the system font." That is exactly the opposite of what the question is about.Firestone
H
1

Here is how I managed to minimize this issue. But note that it only works for Chrome and Edge, otherwise it will fallback to the usual behaviour. By using document.fonts.onloadingdone I check if the font I want is loaded. If it is loaded I create a class which will turn the opacity of the class 'material-icons' to 1 which was initially set as 0 in the css.

On a css file I write this rule (which will get overridden by the class which will be defined in the script eventually)

.material-icons { opacity: 0 };

I added a script tag in my index html page.

  <script>
    // This executes on chrome and EDGE only as tested
    document.fonts.onloadingdone = function (fontFaceSetEvent) {
      // console.log(fontFaceSetEvent.fontfaces) to see the available fonts which have been loaded
      // and change the font family name according to your font family requirement
      const fontName = 'Material Icons';
      if (fontFaceSetEvent.fontfaces.filter(i => i.family === fontName).length > 0) {
        addMakeIconsVisibleClass();
      }
    };

    // Fallback - call below function if not chrome (or EDGE)
    if (navigator.userAgent.toLowerCase().indexOf('chrome') === -1) {
      addMakeIconsVisibleClass();
    }

    function addMakeIconsVisibleClass() {
      let style = document.createElement('style');
      style.innerHTML = '.material-icons { opacity: 1 !important }';
      document.getElementsByTagName('head')[0].appendChild(style);
    }
  </script>
Hypoglycemia answered 20/8, 2021 at 23:47 Comment(0)
S
1

Add this css rule to hide all icons

.material-icons { opacity: 0 };

Then add this js script:

<script>
            if (navigator.userAgent.toLowerCase().indexOf('chrome') !== -1) {
                // only works for non chromium based browsers
                document.fonts.onloadingdone = function (e) {
                    if (e.fontfaces.filter(i => i.family === "Material Icons").length > 0) {
                        showIcons();
                    }
                };
            }else{
                window.onload = function () {
                    showIcons();
                };
            }
            function showIcons() {
                let style = document.createElement('style');
                style.innerHTML = '.material-icons { opacity: 1 !important }';
                document.head.appendChild(style);
            }
        </script>

onloadingdone only works for chromium based browsers, so the alternative for others is use window "onload" event.

Siloa answered 6/1, 2024 at 12:40 Comment(0)
B
0

We can first zero the transparency of the icons using css, then display the icons after 3 seconds using addClass.

var delayInMilliseconds = 3000;
setTimeout(function() {
var element = document.getElementById("mayicons");
  element.className +="ops1";
}, delayInMilliseconds);
.material-icons {
    width: 26px;
    height: 30px;
    overflow: hidden;
    opacity: 0;

}

.ops1{
    opacity: 1;
}
<i id="mayicons" class="material-icons ">list</i>
Brahmin answered 6/3, 2021 at 7:56 Comment(2)
And what if your internet is slow enough so that the font is still not loaded after 3 seconds?Firestone
Using arbitrary waiting times is usually not a good solutionChalaza
T
0

Combining several good proposals, I decided for this solution: Set display:none and replace it later with display:inline-block in the page on-load handler. To avoid layout shifts I added the known icon height, in my case height:64px.

Related style:

.material-icons-round {
    font-family: "Material Icons Round";
    font-weight: normal;
    font-style: normal;
    font-size: 24px;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    display: none;    /* will be replaced once font has loaded */
    transition: opacity 0.24s ease-in-out;
    white-space: nowrap;
    word-wrap: normal;
    direction: ltr;
    width: 21px;
}

Usage (Django template):

<div class="my-card">
  <a href="#" onclick="{{ onclick }}">
    <div class="my-card-content center-align">

      <div class="my-card-title">{{ title }}</div>

      <div style="margin-bottom:8px; height:64px">
        <i class="notranslate material-icons-round medium">{{ icon }}</i>
      </div>

      <div>{{ text }}</div>
    </div>
  </a>
</div>

Page on-load handler:

window.addEventListener("load", function()
{
    // the fonts have been fully loaded, so show the icons
    const icons = document.getElementsByClassName('material-icons-round')
    Array.from(icons).forEach(icon => {
        icon.style.display = 'inline-block'
    });
});
Turnspit answered 9/7, 2023 at 20:32 Comment(0)
A
0

Steps for one more approach:

  • Add style tag (in the head) and define a class .material-icons with style font-size as 0.

    <head>
     <style>
      .material-icons {
        font-size: 0;
      }
    </style>
    
  • Add a link after style tag to load the material font icons.

    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    

Note that the order of steps is important. This way font-size defined in styled tag gets overridden when material font icon stylesheet gets loaded into the browser.

Aegeus answered 22/4, 2024 at 15:52 Comment(0)
L
-1

Create a div style = "position: absolute; top: -1000px" on the homepage and enter all items with class material-icon or awesome font as follows:

<div   style="position:absolute;top:-1000px" >      
<i class="icon material-icons-outlined"  >add_circle</i>
<i class="icon material-icons "  >list_alt</i>
<span class="fas fa-circle fa-stack-2x " ></span>
<span class="fas fa-home fa-stack-1x fa-inverse"  ></span>
</div>
Laburnum answered 20/11, 2019 at 13:30 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.