(论坛最近人气不佳,写点东西水水\~)
脚本注入css,这个大部分人都会,但一般只适用于静态的css,如果想动态修改怎么办?你可能会写这样的代码:
元素.style.display = 'none';
如果还希望加important以提高优先级,得写成这样:
元素.style.setProperty('display', 'none', 'important');
在需要修改的样式特别多的情况下,这种写法非常繁琐,还可能引起页面多次渲染,可以考虑这样写:
元素.style.cssText += 'display: none !important;visibility: hidden !important;';
cssText同时修改多个属性而只渲染一次,+=在遇到已有的属性时,会自动做覆盖处理。以上所有写法都有一个共同的问题:你需要先获取到元素才能设置样式。我们知道元素并不是onload后就全加载完的,有些加载较慢,有些你点了选项才会生成,这也是注入css的一大优势:你不需要管元素何时加载、存不存在,只要写好规则就行了。
css能否做到动态修改呢?移除style再插入新的style?对现有style的文本做正则替换?这也太麻烦了,来看看css的方案:
<div style="--display: none;">
<div id="test" style="display: var(--display) !important;"></div>
</div>
首先在父元素上自定义一个叫--display的属性(自定义属性必须以--开头),然后在子元素中用var()引用该属性的值。注意到了吗?子元素的样式变静态了,意味着可以直接注入css:
GM_addStyle('#test{display: var(--display) !important;}');
自定义属性不仅仅限定在父元素上,只要是父级的都可以,比如直接放在body上,所有后代元素都可以引用到这个值,这样一来,我们就不需要管子元素的加载时机了。当多个父级存在同名定义时,引用遵循就近原则。怎么动态修改呢?自定义属性不能通过style.属性名访问到,但可以setProperty:
documemt.body.style.setProperty('--display', 'none');
或者cssText修改多条:
documemt.body.style.cssText += '--display: none;--visibility: hidden;';
当需要改变样式时,只需改变body上自定义属性的值即可,如此我们就实现了动态修改css而无视加载时机。注意自定义属性的important应该加在目标元素上,加在父元素上的作用是提高同名自定义属性的优先级。自定义属性还可以给多个元素共享,在一些场景下使用也非常方便。
css中calc语句可用于一些简单的计算,比如:
width: calc(100% / 2);
这看起来有点傻,我直接算好了写上去不行吗?所以他一般用在这种场景:
width: calc(100% - 100px);
问题来了,calc可以做动态计算吗?当然可以,我是说,他可以对自定义属性做计算:
width: calc(var(--video-width) / 2);
这个应用场景就比较广泛了,你可以在任意父级元素上定义一个变量(比如视频的宽高),其他元素的大小以该变量为基准,只要一行代码改变变量的值,所有元素都会跟着拉伸。
calc仅能计算数值型的自定义属性(含单位),像前面的--display是字符型,套calc就不行了。另外var不能简单的当成字符串理解(实际是字符串带一个空格),像下面的写法就是错误的:
width: var(--video-width)px
可以由calc改变单位,比如有些属性你希望用px,有些用em,那就可以把自定义属性设为不带单位的数字,然后calc赋予单位:
width: calc(var(--video-width) * 1px);
甚至可以改变原单位,上面的--video-width如果带单位,* 1px也能把单位变成px。
var与calc的结合如果使用得当,可以有效简化我们的脚本代码,你不再需要关注元素何时加载,一行代码也可以影响多个元素的显示效果。Write less, do more!