实现网页换肤的三种办法。
常规做法1
一个全局class
来控制样式,切换皮肤时通过更改全局样式来实现换肤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="../styles/base.css" rel="stylesheet" type="text/css"> <style> .default { color: #000; background: #ddd; } .default .text { border: 1px solid #000; } .green { color: lemonchiffon; background: green; } .green .text { border: 1px solid darkblue; } .red { color: navajowhite; background: red; } .red .text { border: 1px solid pink; } </style> </head> <body> <div class="container"> <div id="skin-container" class="default"> <p class="text">Hello!</p> </div> </div> <div class="input-container"> <label class="input"><input type="radio" value="default" name="skin" checked></label> <label class="input"><input type="radio" value="green" name="skin"></label> <label class="input"><input type="radio" value="red" name="skin"></label> </div> </body> <script> const skinContainer = document.querySelector('#skin-container'); const inputContainer = document.querySelector('.input-container');
const changeSkin = theme => { const { classList } = skinContainer; const { value } = classList; classList.replace(value, theme) };
inputContainer.addEventListener('change', (e) => { changeSkin(e.target.value) }) </script> </html>
|
这种方法的缺点是,加入了一个全局的class
控制样式,提高了样式优先级,如果换肤样式多,代码会非常罗嗦(Less可以简化一部分代码,但是维护还是不方便)
常规做法2
第二种做法是新建多个对应皮肤样式的CSS文件,通过改变<Link>
元素的href
属性来切换皮肤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="../styles/base.css" rel="stylesheet" type="text/css"> <link id="skinLink" href="../styles/default.css" rel="stylesheet" type="text/css"> </head> <body> <div class="container"> <div id="skin-container" class="theme"> <p class="text">Hello!</p> </div> </div> <div class="input-container"> <label class="input"><input type="radio" value="default" name="skin" checked></label> <label class="input"><input type="radio" value="green" name="skin"></label> <label class="input"><input type="radio" value="red" name="skin"></label> </div> </body> <script> const skinLink = document.querySelector('#skinLink'); const inputContainer = document.querySelector('.input-container');
const changeSkin = theme => { skinLink.href = `../styles/${theme}.css` };
inputContainer.addEventListener('change', (e) => { changeSkin(e.target.value) }) </script> </html>
|
几个不同的CSS文件default.css
、red.css
、green.css
中定义不同皮肤的样式
这种方法的缺点是使用JS改变href
属性会带来加载延迟,样式切换不流畅(因为新切换的CSS文件原本没有加载,改变了href
属性后才会加载,会有延迟)
第三种方法
可以借助<link>
标签的rel
属性的alternate
属性值来实现:
1 2 3 4 5 6
| <link href="reset.css" rel="stylesheet" type="text/css">
<link href="default.css" rel="stylesheet" type="text/css" title="默认">
<link href="red.css" rel="alternate stylesheet" type="text/css" title="红色"> <link href="green.css" rel="alternate stylesheet" type="text/css" title="绿色">
|
上面有4个<link>
元素,出现了三种不同性质的CSS文件加载:
- 没有
title
属性,rel
属性值仅仅是stylesheet
的<link>
元素(第一种),无论如何都会加载,可以利用它来加载reset.css
- 有
title
属性,rel
属性值仅仅是stylesheet
的<link>
元素(第二种),作为默认的CSS文件加载并渲染,如default.css
- 有
title
属性,rel
属性值同时包含alternate stylesheet
的<link>
元素(第三种和第四种),作为备选的CSS样式文件加载,但不渲染,通过控制其disabled
属性来确定是否渲染,用来指定皮肤样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="../styles/base.css" rel="stylesheet" type="text/css"> <link id="theme-default" title="默认" href="../styles/default.css" rel="stylesheet" type="text/css"> <link id="theme-green" title="绿色" href="../styles/green.css" rel="stylesheet alternate" type="text/css"> <link id="theme-red" title="红色" href="../styles/red.css" rel="stylesheet alternate" type="text/css"> </head> <body> <div class="container"> <div id="skin-container" class="theme"> <p class="text">Hello!</p> </div> </div> <div class="input-container"> <label class="input"><input type="radio" value="default" name="skin" checked></label> <label class="input"><input type="radio" value="green" name="skin"></label> <label class="input"><input type="radio" value="red" name="skin"></label> </div> </body> <script> const inputContainer = document.querySelector('.input-container'); const links = document.querySelectorAll('link[title]');
const changeSkin = theme => { [...links].forEach(link => { link.disabled = true; link.disabled = link.id !==`theme-${theme}`; }); console.log(links) };
inputContainer.addEventListener('change', (e) => { changeSkin(e.target.value) }) </script> </html>
|
要注意的是,如果具有alternate的元素有两个及以上的disbaled
都为false
,那么哪个都不会加载。
这种方法的优点是:
- 兼容性好
- 语义非常好
- 交互体验好,因为CSS文件被提前加载了,切换皮肤时不会有延迟
文档中的代码在这里。
参考