JavaScript 精确获取 CSS 样式属性与浏览器兼容处理
本文详细介绍如何使用原生JavaScript精确获取元素的CSS样式属性,重点解决跨浏览器(IE与Firefox等)的兼容性问题。内容涵盖RGB与Hex颜色转换、透明度(opacity)处理、长度单位(如em, pt, %)向像素的转换,以及标准模式与怪癖模式下document.body与document.documentElement的行为差异分析。
颜色格式转换与映射处理
实现颜色格式转换的代码如下:
var rgb2hex = function(rgb) {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
return "#"+tohex(rgb[1])+tohex(rgb[2])+tohex(rgb[3])
}
var tohex = function(x) {
var hexDigits = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
return isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
} 上述代码利用正则表达式检测颜色值是否为RGB格式,若是则调用rgb2hex进行转换。然而,当颜色值为名称(如red、green)时,Firefox会将其转换为十六进制格式,而IE则保持原样。为了解决这一差异,我们需要构建一个哈希表,将常用颜色名称映射到对应的十六进制值,并进行逐一匹配。
颜色映射逻辑代码如下:
if(style.search(/background|color/) != -1) {
var color = {
aqua: '#0ff',
black: '#000',
blue: '#00f',
gray: '#808080',
purple: '#800080',
fuchsia: '#f0f',
green: '#008000',
lime: '#0f0',
maroon: '#800000',
navy: '#000080',
olive: '#808000',
orange:'#ffa500',
red: '#f00',
silver: '#c0c0c0',
teal: '#008080',
transparent:'rgba(0,0,0,0)',
white: '#fff',
yellow: '#ff0'
}
if(!!color[value]){
value = color[value]
}
if(value == "inherit"){
return getStyle(el.parentNode,style);
}
if(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.test(value)){
return rgb2hex(value)
}else if(/^#/.test(value)){
value = value.replace('#', '');
return "#" + (value.length == 3 ? value.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : value);
}
return value;
} 至此,CSS样式的精确取值逻辑基本完成。虽然仍存在局限性,但已覆盖了布局及常用样式的需求。代码中提供了一个判断页面渲染模式的常量q,方法命名与jQuery保持一致(仅支持取值,不支持赋值,后续计划与addSheet函数整合)。
核心样式获取库实现
核心样式获取库代码如下:
(function(){
var isQuirk = (document.documentMode) ? (document.documentMode==5) ? true : false : ((document.compatMode=="CSS1Compat") ? false : true);
var isElement = function(el) {
return !!(el && el.nodeType == 1);
}
var propCache = [];
var propFloat = !+"\v1" ? 'styleFloat' : 'cssFloat';
var camelize = function(attr){
return attr.replace(/\-(\w)/g, function(all, letter){
return letter.toUpperCase();
});
}
var memorize = function(prop) { //意思为:check out form cache
return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : camelize(prop));
}
var getIEOpacity = function(el){
var filter;
if(!!window.XDomainRequest){
filter = el.style.filter.match(/progid:DXImageTransform.Microsoft.Alpha\(.?opacity=(.*).?\)/i);
}else{
filter = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
}
if(filter){
var value = parseFloat(filter[1]);
if (!isNaN(value)) {
return value ? value / 100 : 0;
}
}
return 1;
}
var convertPixelValue = function(el, value){
var style = el.style,left = style.left,rsLeft = el.runtimeStyle.left;
el.runtimeStyle.left = el.currentStyle.left;
style.left = value || 0;
var px = style.pixelLeft;
style.left = left;//还原数据
el.runtimeStyle.left = rsLeft;//还原数据
return px + "px"
}
var rgb2hex = function(rgb) {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
return "#"+tohex(rgb[1])+tohex(rgb[2])+tohex(rgb[3])
}
var tohex = function(x) {
var hexDigits = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
return isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
}
var getStyle = function (el, style){
var value;
if(!+"\v1"){
//特殊处理IE的opacity
if(style == "opacity"){
return getIEOpacity(el)
}
value = el.currentStyle[memorize(style)];
//特殊处理IE的height与width
if (/^(height|width)$/.test(style)){
var values = (style == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
if(isQuirk){
return el[camelize("offset-"+style)] + "px"
}else{
var client = parseFloat(el[camelize("client-"+style)]),
paddingA = parseFloat(getStyle(el, "padding-"+ values[0])),
paddingB = parseFloat(getStyle(el, "padding-"+ values[1]));
return (client - paddingA - paddingB)+"px";
}
}
}else{
if(style == "float"){
style = propFloat;
}
value = document.defaultView.getComputedStyle(el, null).getPropertyValue(style)
}
//下面部分全部用来转换上面得出的非精确值
if(!/^\d+px$/.test(value)){
//转换可度量的值
if(/(em|pt|mm|cm|pc|in|ex|rem|vw|vh|vm|ch|gr)$/.test(value)){
return convertPixelValue(el,value);
}
//转换百分比,不包括字体
if(/%$/.test(value) && style != "font-size"){
return parseFloat(getStyle(el.parentNode,"width")) * parseFloat(value) /100 + "px"
}
//转换border的thin medium thick
if(/^(border).+(width)$/.test(style)){
var s = style.replace("width","style"),
b = {
thin:["1px","2px"],
medium:["3px","4px"],
thick:["5px","6px"]
};
if(value == "medium" && getStyle(el,s) == "none"){
return "0px";
}
return !!window.XDomainRequest ? b[value][0] : b[value][1];
}
//转换margin的auto
if(/^(margin).+/.test(style) && value == "auto"){
var father = el.parentNode;
if(/MSIE 6/.test(navigator.userAgent) && getStyle(father,"text-align") == "center"){
var fatherWidth = parseFloat(getStyle(father,"width")),
_temp = getStyle(father,"position");
father.runtimeStyle.postion = "relative";
var offsetWidth = el.offsetWidth;
father.runtimeStyle.postion = _temp;
return (fatherWidth - offsetWidth)/2 + "px";
}
return "0px";
}
//转换top|left|right|bottom的auto
if(/(top|left|right|bottom)/.test(style) && value == "auto"){
return el.getBoundingClientRect()[style];
}
//转换颜色
if(style.search(/background|color/) != -1) {
var color = {
aqua: '#0ff',
black: '#000',
blue: '#00f',
gray: '#808080',
purple: '#800080',
fuchsia: '#f0f',
green: '#008000',
lime: '#0f0',
maroon: '#800000',
navy: '#000080',
olive: '#808000',
orange:'#ffa500',
red: '#f00',
silver: '#c0c0c0',
teal: '#008080',
transparent:'rgba(0,0,0,0)',
white: '#fff',
yellow: '#ff0'
}
if(!!color[value]){
value = color[value]
}
if(value == "inherit"){
return getStyle(el.parentNode,style);
}
if(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/.test(value)){
return rgb2hex(value)
}else if(/^#/.test(value)){
value = value.replace('#', '');
return "#" + (value.length == 3 ? value.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : value);
}
return value;
}
}
return value;//如 0px
}
var css = function(){
var a = arguments;
if(a.length == 1){
return getStyle(this,a[0])
}
}
var _ = function(el){
var el = isElement(el)? el :document.getElementById(el);
var gene = !el.constructor ? el : el.constructor.prototype;
gene.css = css;
gene.width = function(){
return getStyle(this,"width");
};
gene.height = function(){
return getStyle(this,"height");
};
return el
}
if(!window._){ //为了避免与JQuery的$发生冲突,我用_作为类库唯一的全局变量
window['_'] =_;
}
_.q = isQuirk;
})() 样式获取库调用示例
用法如下:
调用示例代码如下:
window.onload = function(){
alert(_("ccc").css("background-color"))
alert(_("aaa").css("width"))
alert(_(document.body).width())
}; 标准模式与怪癖模式下的元素行为差异
我们可以利用该工具深入分析document.body与document.documentElement在标准模式下的行为差异。
对比测试代码如下:
function text(){
var body = document.body,html = document.documentElement;
_("w1").innerHTML = _(body).width();
_("w2").innerHTML = _(html).width();
_("h1").innerHTML = _(body).height();
_("h2").innerHTML = _(html).height();
_("ml1").innerHTML = _(body).css("margin-left");
_("ml2").innerHTML = _(html).css("margin-left");
_("mr1").innerHTML = _(body).css("margin-right");
_("mr2").innerHTML = _(html).css("margin-right");
_("mt1").innerHTML = _(body).css("margin-top");
_("mt2").innerHTML = _(html).css("margin-top");
_("mb1").innerHTML = _(body).css("margin-bottom");
_("mb2").innerHTML = _(html).css("margin-bottom");
_("pl1").innerHTML = _(body).css("padding-left");
_("pl2").innerHTML = _(html).css("padding-left");
_("pr1").innerHTML = _(body).css("padding-right");
_("pr2").innerHTML = _(html).css("padding-right");
_("bl1").innerHTML = _(body).css("border-left-width");
_("bl2").innerHTML = _(html).css("border-left-width");
_("br1").innerHTML = _(body).css("border-right-width");
_("br2").innerHTML = _(html).css("border-right-width");
_("qqq").innerHTML = !_.q ? "标准模式" : "怪癖模式";
_("t1").innerHTML = _(body).css("top");
_("t2").innerHTML = _(html).css("top");
_("l1").innerHTML = _(body).css("left");
_("l2").innerHTML = _(html).css("left");
_("ot1").innerHTML = body.offsetTop;
_("ot2").innerHTML = html.offsetTop;
_("ol1").innerHTML = body.offsetLeft;
_("ol2").innerHTML = html.offsetLeft;
_("ct1").innerHTML = body.clientTop;
_("ct2").innerHTML = html.clientTop;
_("cl1").innerHTML = body.clientLeft;
_("cl2").innerHTML = html.clientLeft;
_("cw1").innerHTML = body.clientWidth;
_("cw2").innerHTML = html.clientWidth;
_("ow1").innerHTML = body.offsetWidth;
_("ow2").innerHTML = html.offsetWidth;
_("sw1").innerHTML = body.scrollWidth;
_("sw2").innerHTML = html.scrollWidth;
} <!doctype html> <html dir="ltr" lang="zh-CN" id="html"> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=8"> <style type="text/css"> html, body{ border: 0; } </style> <title>精确取值</title> </head> <body id="body"> <table border="1"> <thead> <th>测试属性</th><th>document.body</th><th>document.documentElement</th> </thead> <tbody> <tr><th>width</th><td><span id="w1"></span></td><td><span id="w2"></span></td></tr> <tr><th>height</th><td><span id="h1"></span></td><td><span id="h2"></span></td></tr> <tr><th>margin-left</th><td><span id="ml1"></span></td><td><span id="ml2"></span></td></tr> <tr><th>margin-right</th><td><span id="mr1"></span></td><td><span id="mr2"></span></td></tr> <tr><th>margin-top</th><td><span id="mt1"></span></td><td><span id="mt2"></span></td></tr> <tr><th>margin-bottom</th><td><span id="mb1"></span></td><td><span id="mb2"></span></td></tr> <tr><th>padding-left</th><td><span id="pl1"></span></td><td><span id="pl2"></span></td></tr> <tr><th>padding-right</th><td><span id="pr1"></span></td><td><span id="pr2"></span></td></tr> <tr><th>border-left-width</th><td><span id="bl1"></span></td><td><span id="bl2"></span></td></tr> <tr><th>border-right-width</th><td><span id="br1"></span></td><td><span id="br2"></span></td></tr> <tr><th>渲染模式</th><td colspan='2'><span id="qqq"></span></td></tr> <tr><th>top</th><td><span id="t1"></span></td><td><span id="t2"></span></td></tr> <tr><th>left</th><td><span id="l1"></span></td><td><span id="l2"></span></td></tr> <tr><th>offsetTop</th><td><span id="ot1"></span></td><td><span id="ot2"></span></td></tr> <tr><th>offsetLeft</th><td><span id="ol1"></span></td><td><span id="ol2"></span></td></tr> <tr><th>clientTop</th><td><span id="ct1"></span></td><td><span id="ct2"></span></td></tr> <tr><th>clientLeft</th><td><span id="cl1"></span></td><td><span id="cl2"></span></td></tr> <tr><th>offsetWidth</th><td><span id="ow1"></span></td><td><span id="ow2"></span></td></tr> <tr><th>clientWidth</th><td><span id="cw1"></span></td><td><span id="cw2"></span></td></tr> <tr><th>scrollWidth</th><td><span id="sw1"></span></td><td><span id="sw2"></span></td></tr> </tbody> </table> <p><button type="button" onclick="text()" >运行代码</button></p> <br> <br><br> <br><br> <br><br> <br><br> <br><br> <br> </body> </html> [Ctrl+A 全选 注:引入外部Js需再刷新一下页面才能执行]
在标准模式下,Firefox等浏览器中offsetWidth最大值约为1007,因为offsetWidth不大于clientWidth,而clientWidth不包含滚动条(滚动条宽度固定为17px)。在IE中,offsetWidth比clientWidth多出两个border宽度。通过计算1024-1003-17=4,可知这4px由两个auto生成的border组成,且在IE中这两个border宽度固定为2px,无法通过常规手段修改。
样式定义代码如下:
<style type="text/css">
html{
border: 0;
}
</style> 换言之,在标准模式下,IE的html元素存在一个不可修改的、宽度为2px的border。这也意味着上述程序存在一个BUG,未能正确显示html的border为2px。
再看怪癖模式,
Firefox等浏览器没有所谓的怪癖模式,主要观察IE的表现。发现那个神秘的2px再次出现,此时体现在document.body的clientTop和clientLeft中。那么怪癖模式下document.body的clientTop和clientLeft对应CSS中的什么概念呢?参考微软早期的一张图表,当时IE5独步天下,没有标准模式与怪癖模式之分,因此该图表内容均按怪癖模式表示。

可以看出,clientLeft相当于borderLeft,clientTop相当于borderTop。至于前文提到的border-left-width与border-right-width,由于未考虑标准模式与怪癖模式下的差异,结果可能存在错误。既然document.body的边框区域已达1024px,html元素的位置如何安排?在微软早期的设想中,body元素才代表文档(有力证据是怪癖模式下滚动条位于body元素内)。标准模式及Firefox等浏览器支持的“标准”是在微软极不情愿下引入的。documentElement这个名字显然不是微软的风格,若由微软命名,应更简洁如html或body。在标准模式下,获取scrollLeft需使用document.documentElement.scrollLeft,因为此时body不存在滚动条;而在怪癖模式下,需使用document.body。尽管微软通过补丁添加了document.documentElement(名称拗口,这也解释了网景为何失败),但滚动条的位置并非随意改变。
https://www.jb51.net/article/21719.htm
anime.js 快速入门:怎么引入库并编写第一个 DOM 动画
2026-11-05 14:22:43
在线JS解密后变量名仍乱码?结合浏览器DevTools调试还原
2026-11-04 20:26:01
怎么在浏览器中运行JavaScript代码并查看输出结果
2026-11-04 09:07:33
JS 获取屏幕分辨率:window.screen.width 与 height 用法
2026-11-04 08:52:48
为什么推荐用DOMContentLoaded代替window.onload初始化JS
2026-11-04 08:42:33
