Tobias Glaus
Tobias Glaus

Reputation: 3628

jQuery color replacement working sometimes and sometimes not

I got a little webpage, which you can find here: http://gabrielaelona18.esy.es/

With a CMS the user can replace the "theme-color". So a little script replaces every color with the hex code #16a085, no matter if background-color, border-bottom-color or whatever. That's the code:

$(function(){
        $('html').addClass('notransition');
        function colorReplace(findHexColor, replaceWith) {
          // Convert rgb color strings to hex
          function rgb2hex(rgb) {
            if (/^#[0-9A-F]{6}$/i.test(rgb)) return rgb;
            rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
            function hex(x) {
              return ("0" + parseInt(x).toString(16)).slice(-2);
            }
            return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
          }

          // Select and run a map function on every tag
          $('*').map(function(i, el) {
            // Get the computed styles of each tag
            var styles = window.getComputedStyle(el);

            // Go through each computed style and search for "color"
            Object.keys(styles).reduce(function(acc, k) {
              var name = styles[k];
              var value = styles.getPropertyValue(name);
              if (value !== null && name.indexOf("color") >= 0) {
                // Convert the rgb color to hex and compare with the target color
                if (value.indexOf("rgb(") >= 0 && rgb2hex(value) === findHexColor) {
                  // Replace the color on this found color attribute
                  $(el).css(name, replaceWith);
                }
              }
            });
          });
        }
        // Call like this for each color attribute you want to replace
        colorReplace("#16a085", "#456780");
});

The thing is, that sometimes it works, but sometimes it just doesn't. You can visit the website, which I mentioned above. If its not blue, just reload a couple times, until you can see blue.

Upvotes: 0

Views: 98

Answers (2)

K. Rohde
K. Rohde

Reputation: 9676

You are having a network (page load) problem. Go check out your website loading with (Firefox) Developer Toolbar on the tab Network.

There you can see the following coincidences:

enter image description here enter image description here

The blue line shows the point in time where the document triggers an event, that the whole content of the page is loaded. (Specifics here.) This is called the DOMContentLoaded Event. See here for further details. As described in the linked page:

The DOMContentLoaded event is fired when the [...] document has been completely loaded and parsed, without waiting for stylesheets [...] to finish loading.

So this means, if the stylesheet makes it in time, the function you presented can manage to override the colors. If not, the function does not even find the corresponding color codes, because they are not there yet. Pretty simple.

This in turn means, that you should wait for your stylesheet to load before the function call. From the jQuery documentation, even $(document).ready() might not be enough, and instead you will have to use $(window).on("load", function() { ... }) to secure everything is ready. To have a clue about the load event, it is marked as the red line in the network monitor.

If this, for any reason, is not working out for you, you could of course move your color styles from the .css file into the html document as a style tag. Then, I guess, it will be available on DOMContentLoaded.

Upvotes: 2

JasperZelf
JasperZelf

Reputation: 2844

It looks like a timing issue to me.

I see the the green screen consistently when I have chrome dev-tools open, and I see a blue screen when I close it.

If I place a breakpoint at the top of your script, a$(function() at the top of your script, and once the CSS has been loaded continue, it will work as expected again.

So in order to fix your problem, you should only run colorReplace("#16a085", "#456780"); when your stylesheets have been parsed.

EDIT:

Acording to this article simply putting the stylesheets above your javascript might do the trick for you.

According to HTML5, DOMContentLoaded is a plain DOM ready event without taking stylesheets into account. However, the HTML5 parsing algorithm require browsers to defer the execution of scripts until all previous stylesheets are loaded. Let’s get back to the example from testcase #2:

<link rel="stylesheet" href="http://molily.de/weblog/stylesheet.css"> <script src="http://molily.de/weblog/script.js"></script> When the HTML5 parser encounters the ... tags, the whole parsing process is halted. First, the browser fetches the script resource, given it’s an external script. Second, the browser waits for the preceding stylesheet to load. Third, the JavaScript engine runs the downloaded script code. Finally, the parser continues to parse the HTML document.

EDIT 2

This answer looks like it confirms the solution in my first edit:

is-document-ready-also-css-ready?

Upvotes: 1

Related Questions