内容提要
文章介绍一种无需修改CSS源码、用JavaScript将px转换为rem的方法:遍历document.styleSheets,修改CSSStyleRule的style.cssText,把px替换为rem;同时动态设置根字号,并监听resize和pageshow事件重新计算,转换前先隐藏根元素以避免闪烁。
延伸解读
转换范围需手动控制
代码通过检查link标签上的px2rem属性来决定是否转换该样式表,避免影响所有样式。若想全部转换,需注释掉判断逻辑。这种设计让开发者能选择性处理特定CSS文件,但需提前在HTML中标记,否则默认不转换。
根字号动态计算与事件监听
根字号设置为视口宽度的十分之一,即1rem等于屏幕宽度的10%。resize事件在窗口尺寸变化时重新计算,pageshow事件则处理从缓存恢复页面的情况,确保rem基准值始终与当前视口匹配。
隐藏根元素避免布局闪烁
转换前将根元素display设为none,待DOMContentLoaded后完成所有样式替换再恢复显示。这防止了页面先以px渲染再突变为rem导致的布局跳动,但会短暂空白,需权衡体验。
仅处理普通样式规则
循环中通过item.type != 1跳过非CSSStyleRule的规则,如@keyframes或@media内的规则。因此动画关键帧或媒体查询中的px不会被转换,需额外处理或注意兼容性。
Q&A
如何在不修改CSS源码的情况下把px单位批量转换成rem?
可以通过JavaScript遍历document.styleSheets,修改每个CSSStyleRule的style.cssText,用正则将px值替换为rem值。同时动态设置根字号,并监听resize和pageshow事件重新计算。转换前先隐藏根元素以避免页面闪烁。
用JS转换px到rem时,如何避免页面布局闪烁?
在转换开始前将根元素(document.documentElement)的display设为'none',等所有CSS规则转换完成后再设为'initial',这样页面不会在转换过程中被渲染,从而避免布局突然变化造成的闪烁。
动态设置根字号(1rem等于多少px)的代码逻辑是什么?
通过setRemUnit函数,将根元素的fontSize设置为docEl.clientWidth / 10 + 'px',即1rem等于视口宽度的十分之一。初始化时调用一次,并在window的resize事件和pageshow事件(当e.persisted为true时)中重新调用,以适应尺寸变化和页面缓存恢复。
如何只转换特定link标签引入的CSS,而不影响其他样式?
在需要转换的link标签上添加自定义属性,例如px2rem="true",然后在遍历document.styleSheets时,检查sheet.ownerNode.getAttribute('px2rem')是否为null,如果为null则跳过该样式表。这样只处理标记过的样式表。
转换时px到rem的换算比例是如何确定的?
代码中定义了defWidth = 414 / 10,即设计稿宽度414px对应10rem,所以换算比例为1rem = 41.4px。替换时用px值除以defWidth得到rem值,并保留最多5位小数(通过toFixed(5)处理有小数的情况)。
为什么要在DOMContentLoaded事件中执行CSS替换?
因为需要等待styleSheets初始化完成,确保能获取到所有CSS规则。在DOMContentLoaded触发时,样式表通常已加载完毕,此时遍历并修改cssText才能生效。同时配合根元素隐藏,避免在样式未就绪时渲染页面。