0

I'm trying to make a web page "static", with all styles as inline. For this I first save all computed styles, then remove all applied style (class and css files), check the difference with saved styles and add inline for missing styles. This is my function:

var allstyles=null;
var allelms=null;
function FixHTML()
{
    if (!allstyles)
    {
        console.log("remove scripts");
        var elms=document.getElementsByTagName('SCRIPT');
        for(var i=elms.length-1;i>-1;i--)
        {
            var elm=elms[i];

            if (elm.tagName=="SCRIPT" && !elm.innerHTML.match(/FixHTML/))
            {
                elm.parentElement.removeChild(elm);
            }
        }

        //sauvegarde des styles
        console.log("save styles");
        allstyles=[];

        allelms=document.getElementsByTagName('*');

        for(i=0;i<allelms.length;i++)
        {
            elm=allelms[i];
            if (!elm.id)
                elm.id="tmpid"+i;
            var id=elm.id;

            allstyles[id]=[];

            var style=getStyleObject(elm);
            for(var key in style)
            {
                allstyles[id][key]=style[key];
            }

            if (elm.className)
                elm.className="";
        }

        console.log("delete stylesheet links");
        elms=document.getElementsByTagName('LINK');
        for(i=elms.length-1;i>-1;i--)
        {
            elm=elms[i];

            console.log(elm.href);
            if (elm.rel="stylesheet")
                elm.href="nowhere";
        }
    }
    setTimeout(RestoreClassStyles,2000);
}

function RestoreClassStyles()
{
    console.log("restore class styles",allstyles);

    allelms=document.getElementsByTagName('*');

    for(var i=0;i<allelms.length;i++)
    {
        var elm=allelms[i];
        var id=elm.id;

        var style=getStyleObject(elm);
        for(var key in allstyles[id])
        {
            if (allstyles[id][key]!=style[key])
            {
                console.log(key);
                elm.style[key]=allstyles[id][key];
            }
        }
    }
}

function getStyleObject(dom){
        var style;
        var returns = {};
        if(window.getComputedStyle){
            var camelize = function(a,b){
                return b.toUpperCase();
            };
            style = window.getComputedStyle(dom, null);
            for(var i = 0, l = style.length; i < l; i++){
                var prop = style[i];
                var camel = prop.replace(/\-([a-z])/g, camelize);
                var val = style.getPropertyValue(prop);

                returns[camel] = val;
            };

            return returns;
        };
        console.log("not found",elm);
        return "";
    }

It "works a bit" but some styles are incorrect so I need help to find what I'm missing in that code. Working example: http://jsfiddle.net/owecfdr2/

Entretoize
  • 2,124
  • 3
  • 23
  • 44
  • 2
    If the goal is a self-contained HTML file, why not just use a `style` element instead of a `link` element? What is gained by using inline styles? – T.J. Crowder Nov 26 '18 at 11:19
  • *"It "works a bit" but some styles are incorrect so I need help to find what I'm missing in that code."* If you do really need to do this, please update your question with a [mcve] demonstrating the problems (the places where it isn't quite working) using a **runnable** example with Stack Snippets (the `[<>]` toolbar button; [here's how to do one](https://meta.stackoverflow.com/questions/358992/)). – T.J. Crowder Nov 26 '18 at 11:21
  • @T.J.Crowder I just added a working example. – Entretoize Nov 26 '18 at 12:01
  • That's great! But please do read the links above again. The whole thing must be **here**, on-site, not just linked. Two reasons: People shouldn't have to go off-site to help you; and links rot, making the question and its answers useless to people in the future. That's why I pointed to Stack Snippets. – T.J. Crowder Nov 26 '18 at 12:32

1 Answers1

4

Instead of using inline styles, I would convert the link elements to style elements. You can do that like this (I've stuck to ES5 because it looked like you were doing so in your code):

function linkToStyle(link) {
    var css = [];
    var sheet = link.sheet;
    var rules = sheet.cssRules || sheet.rules;
    for (var i = 0; i < rules.length; ++i) {
        var rule = rules[i];
        css.push(rule.cssText);
    }
    var style = document.createElement("style");
    style.type = "text/css";
    style.appendChild(
        document.createTextNode(css.join("\r\n"))
    );
    return style;
}
document.querySelectorAll("link[rel=stylesheet]").forEach(function(link) {
    var style = linkToStyle(link);
    var parent = link.parentNode;
    parent.insertBefore(style, link);
    parent.removeChild(link);
});

That finds all the link elements with rel="stylesheet" and replaces them with style elements with the same CSS text, by getting the CSS stylesheet object from the link element, looping through its CSS rules, and using the cssText of each rule.

As misorude points out in a comment, any URLs in the linked CSS stylesheets are relative to the stylesheet, not the page linking to the style sheet, so if your stylesheets and page are not in the same place from a URL perspective (e.g., same directory), it gets a bit more complicated: you'll have to adjust the URLs to account for that. A simple version would just handle rules of type CSSRule.STYLE_RULE (which are CSSStyleRules), looping through the properties to find and adjust URLs. (If there are imports, it gets more complicated.)


That code relies on forEach on the NodeList returned by querySelectorAll. Most browsers have that now; this answer shows how to detect if the browser doesn't and polyfill it.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 2
    _“I would convert the link elements to style elements”_ - is that enough though? I would assume this to easily lead to problems with relative URLs, like for background images and such - those used to be relative to the location of the stylesheet, but when you switch from `link` to `style`, that info gets lost. (Or is there any “magic” implemented within cssRules/cssText that would automatically account for that, and correct such relative references?) – misorude Nov 26 '18 at 11:39
  • @misorude - The relative URLs thing is a **very** good point. I've added a note. – T.J. Crowder Nov 26 '18 at 11:46
  • I used that solution before you replied to my question, and corrected for the relative image, fonts... It works. Thanks – Entretoize Nov 26 '18 at 20:07