如何使用Javascript加载CSS文件?

399

是否可以使用JavaScript将CSS样式表导入到HTML页面中?如果可以,如何实现?

附注:JavaScript将托管在我的网站上,但我希望用户能够将其放入其网站的标签中,并且应该能够将托管在我的服务器上的CSS文件导入到当前网页中(CSS文件和JavaScript文件都将托管在我的服务器上)。


还有一个关于jQuery的问题:https://dev59.com/JXE85IYBdhLWcg3wnVCp - jcubic
20个回答

538
这是一种“老派”的方法,希望在所有浏览器上都能正常工作。理论上,您可以使用setAttribute,但不幸的是,IE6不支持它的一致性。
var cssId = 'myCss';  // you could encode the css path itself to generate id..
if (!document.getElementById(cssId))
{
    var head  = document.getElementsByTagName('head')[0];
    var link  = document.createElement('link');
    link.id   = cssId;
    link.rel  = 'stylesheet';
    link.type = 'text/css';
    link.href = 'http://website.example/css/stylesheet.css';
    link.media = 'all';
    head.appendChild(link);
}

这个例子检查CSS是否已经添加,如果没有则只添加一次。

将该代码放入JavaScript文件中,让最终用户简单地包含JavaScript,并确保CSS路径是绝对的,以便从您的服务器加载。

VanillaJS

以下是一个示例,它使用纯JavaScript根据URL的文件名部分将CSS链接注入到head元素中:

<script type="text/javascript">
var file = location.pathname.split( "/" ).pop();

var link = document.createElement( "link" );
link.href = file.substr( 0, file.lastIndexOf( "." ) ) + ".css";
link.type = "text/css";
link.rel = "stylesheet";
link.media = "screen,print";

document.getElementsByTagName( "head" )[0].appendChild( link );
</script>

将代码插入在结束的head标签之前,CSS将在页面呈现之前加载。使用外部JavaScript(.js)文件会导致未样式化内容的 Flash( FOUC)出现。

9
当 CSS 文件加载完成后运行回调函数怎么样? - jchook
2
这个要可靠地完成就比较复杂了。现在流行的JavaScript库通常都有一些形式的回调函数来加载JavaScript和样式表。例如,可以看看YUI Get - user58777
10
也许最好使用一个其他的字符作为文档快捷方式,因为它可能会很容易地干扰 jQuery(就像在我的情况下一样) ;) - Zathrus Writer
2
@jonnybojangles 使用jQuery.noConflict意味着将所有对jQuery的引用重命名为$。使用不同的变量名称作为CSS加载器会更简单。 - tommypyatt
2
@jchook 我成功地通过在添加之前加入 link.onload = function(){ ... } 来附加回调函数。 - Lounge9
显示剩余2条评论

90
如果您使用jQuery:
$('head').append('<link rel="stylesheet" type="text/css" href="style.css">');

4
只有在页面加载之前注入样式表才能起效,如果我在已经加载了我在Dropbox上托管的CSS文件的页面内运行此命令,则无法起作用。你有什么想法可以使这个命令起作用吗?$('head').append('<link rel="stylesheet" type="text/css" href="https://dl.dropboxusercontent.com/s/ep1nzckmvgjq7jr/remove_transitions_from_page.css">'); - dcts

56

我猜这个脚本大概会做以下的事情:

<script type="text/javascript" src="/js/styles.js"></script>
这个JS文件包含以下语句:
if (!document.getElementById) document.write('<link rel="stylesheet" type="text/css" href="/css/versions4.css">');

如果Javascript和CSS文件需要引用你的站点,它们的地址必须是绝对路径。

许多CSS导入技术在这篇“Say no to CSS hacks with branching techniques”文章中有所讨论。

“Using JavaScript to dynamically add Portlet CSS stylesheets”文章还提到了使用CreateStyleSheet方法(IE专有方法)的可能性:

<script type="text/javascript">
//<![CDATA[
if(document.createStyleSheet) {
  document.createStyleSheet('http://server/stylesheet.css');
}
else {
  var styles = "@import url(' http://server/stylesheet.css ');";
  var newSS=document.createElement('link');
  newSS.rel='stylesheet';
  newSS.href='data:text/css,'+escape(styles);
  document.getElementsByTagName("head")[0].appendChild(newSS);
}
//]]>

这是我想到的为那些没有document.createStyleSheet()方法的浏览器添加该方法的解决方案:https://dev59.com/l3RB5IYBdhLWcg3wv5tA#524798 - Christoph
1
不确定在哪种情况下,但在某些情况下,不建议使用document.write,因为它会覆盖您网站的整个主体。 - Eduard Luca
@VonC,有没有直接获取<style>元素的函数,而不是通过("head")[0]假设它是<head>的第一个子元素? - Pacerier
@Pacerier 我相信你会在 https://groups.google.com/forum/#!topic/prototype-scriptaculous/Bs_JYNpoC-k 中找到相关讨论。例如:var style = document.getElementsByTagName("style")[0]; - VonC

39

Element.insertAdjacentHTML具有非常好的浏览器支持,并且可以在一行中添加样式表。

document.getElementsByTagName('head')[0].insertAdjacentHTML(
    'beforeend',
    '<link rel="stylesheet" href="path/to/style.css" />');

22

如果你想知道(或等待)直到样式本身加载完成,这个方法可行:

// this will work in IE 10, 11 and Safari/Chrome/Firefox/Edge
// add ES6 poly-fill for the Promise, if needed (or rewrite to use a callback)

let fetchStyle = function(url) {
  return new Promise((resolve, reject) => {
    let link = document.createElement('link');
    link.type = 'text/css';
    link.rel = 'stylesheet';
    link.onload = () => resolve();
    link.onerror = () => reject();
    link.href = url;

    let headScript = document.querySelector('script');
    headScript.parentNode.insertBefore(link, headScript);
  });
};

使用方法:

fetchStyle(url)
 .then(
   () => console.log("style loaded succesfully"),
   () => console.error("style could not be loaded"),
);

如何使用这个? - Raj Nandan Sharma
你可以使用fetchStyle(url).then(function() {stuff goes here}),并了解一下Promise的相关知识。 - Ben Taliadoros

15

使用此代码:

var element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", "external.css");
document.getElementsByTagName("head")[0].appendChild(element);

13
在现代浏览器中,您可以这样使用promise。创建带有promise的加载器函数:

在现代浏览器中,您可以像这样使用promise。创建一个带有promise的加载器函数:

function LoadCSS( cssURL ) {

    // 'cssURL' is the stylesheet's URL, i.e. /css/styles.css

    return new Promise( function( resolve, reject ) {

        var link = document.createElement( 'link' );

        link.rel  = 'stylesheet';

        link.href = cssURL;

        document.head.appendChild( link );

        link.onload = function() { 

            resolve(); 

            console.log( 'CSS has loaded!' ); 
        };
    } );
}

显然,您希望在CSS加载后执行某些操作。您可以像这样调用需要在CSS加载后运行的函数:

LoadCSS( 'css/styles.css' ).then( function() {

    console.log( 'Another function is triggered after CSS had been loaded.' );

    return DoAfterCSSHasLoaded();
} );

如果想深入了解它的工作原理,以下链接可能会有所帮助:

Promise 官方文档

Promise 有用指南

Promise 入门视频


10

我知道这是一个相当旧的帖子,但是我想说几句。

根据您的需求,还有另一种方法可以实现此目的。

我有一个需要仅在一段时间内激活css文件的情况,就像切换css一样。激活css,然后在另一个事件之后取消激活。

与其动态加载然后删除css,不如在新css中所有元素前面添加类/ id,然后只切换css的基节点(如body标签)的该类/ id。

使用此解决方案,您将会有更多的css文件最初加载,但您将拥有更加灵活的切换css布局的方式。


8

你听说过Promise吗?它们在所有现代浏览器上运行,并且相对容易使用。看一下这个简单的方法来将CSS注入到HTML头部:

function loadStyle(src) {
    return new Promise(function (resolve, reject) {
        let link = document.createElement('link');
        link.href = src;
        link.rel = 'stylesheet';

        link.onload = () => resolve(link);
        link.onerror = () => reject(new Error(`Style load error for ${src}`));

        document.head.append(link);
    });
}

你可以按照以下方式实现:
window.onload = function () {
    loadStyle("https://fonts.googleapis.com/css2?family=Raleway&display=swap")
        .then(() => loadStyle("css/style.css"))
        .then(() => loadStyle("css/icomoon.css"))
        .then(() => {
            alert('All styles are loaded!');
        }).catch(err => alert(err));
}

这真的很酷,对吧?这是使用 Promises 来决定样式优先级的一种方法。

要查看多样式加载实现,请参见:https://dev59.com/12Up5IYBdhLWcg3wHUvi#63936671


7

这是一个使用纯JavaScript根据URL文件名的部分,将CSS链接注入到head元素中的一行示例:

document.head.innerHTML += '<link rel="stylesheet" href="css/style.css">';

大多数浏览器都支持。参见浏览器兼容性


使用这个“一行代码”将会导致在用户多次加载页面(例如,通过ajax加载的子表单)时,头部会添加多个内容。 - OSWorX
1
@OSWOrX 防止多次添加不是问题的一部分,但您可以通过为元素分配“id”并仅在不存在时添加它来检查它是否已经存在于DOM中。 - Iglesias Leonardo
1
这是最简短和最好的答案。大多数答案没有提供避免重复的逻辑。只需将此行代码放入一个函数中,并使用ID或变量检查该函数是否已被调用即可。 - AxeEffect
这绝对是个糟糕的主意。它会导致整个 head 的 DOM 被字符串化,然后再次解析,这不仅浪费了大量不必要的计算资源,还会丢失在 HTML 代码中没有持久化的任何属性,比如事件处理程序,并且可能会再次运行所有的 JavaScript。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接