JavaScript动态添加CSS样式规则与W3C校验兼容方案
本文探讨在Web开发中动态生成与修改CSS样式规则的方法。通过解析DOM2.0中的CSSStyleSheet接口,介绍如何获取和操作外部及内部样式表。重点分析不同浏览器对样式规则的差异支持,特别是IE与标准浏览器的兼容性问题,并提供一个增强版addSheet函数,实现自动处理透明度(opacity)与IE滤镜的转换,以通过W3C校验。
动态样式规则的必要性
遵循表现与结构分离的原则,直接引入独立样式表通常是最佳方案。然而,在某些交互场景下,例如实现可拖动的DIV元素,这种方法可能不适用。从样式角度看,拖动效果需要将该元素设置为绝对定位,以避免影响原有文档流,并通过不断改变其top和left属性值来实现移动。由于拖动过程涉及时间维度(如每秒24帧),预先编写所有可能的样式规则既不现实也不高效。因此,动态生成和快速修改样式规则显得尤为重要。为此,W3C在DOM2.0标准中扩充了大量相关接口。
即便不考虑表现与结构分离的严格定义,引入样式表也并非唯一途径。实际上,网页中存在三种主要的样式类型:外部样式、内部样式和内联样式。
外部样式指的是将CSS代码编写在独立的.css文件中,并通过link标签引入。
内部样式是指将CSS代码直接写在一个style标签内,通常放置在head标签中。本文后续提供的函数所生成的样式即属于此类。
内联样式则是直接写在HTML元素的style属性中的样式定义。
新添加的接口主要应用于外部样式表领域。之所以称之为接口,是因为具体的实现由浏览器厂商提供。例如,IE6等旧版浏览器对这些标准接口的支持极为有限,甚至完全忽略。
CSSStyleSheet对象与接口详解
在W3C模型中,type属性为"text/css"的link标签和style标签均代表一个CSSStyleSheet对象。我们可以通过document.styleSheets属性获取当前页面中所有的CSSStyleSheet对象。需要注意的是,这是一个集合对象,而非普通的数组。每个CSSStyleSheet对象包含以下属性:
* type:始终返回字符串“text/css”。
* disabled:作用与input元素的disabled属性相同,默认值为false。
* href:返回样式表的URL地址;如果是style标签,则返回null。
* title:返回其title属性的值,该属性与普通元素的title属性无异,可自由设定。
* media:IE与Firefox返回的内容不一致,难以统一描述。media属性用于规定样式规则适用的设备类型,默认适用于所有设备。
* ownerRule:如果样式表是通过@import引入的,则返回一个只读的CSSRule对象。
* cssRules:返回一个只读的样式规则对象(CSSStyleRule object)集合。
样式规则对象(CSSStyleRule object)是W3C为了更精细地控制样式而设计的概念。例如,以下代码段对应一个样式规则对象:
代码如下:
button[type] {
padding:4px 10px 4px 7px;
line-height:17px;
} 样式规则对象主要包含以下属性:type、cssText、parentStyleSheet和parentRule。
type属性类似于节点的nodeType,用于对样式规则进行细分,其值是一个表示类型的整数。具体对应关系如下:
* 0: CSSRule.UNKNOWN_RULE
* 1: CSSRule.STYLE_RULE (定义一个CSSStyleRule对象)
* 2: CSSRule.CHARSET_RULE (定义一个CSSCharsetRule对象,用于设定当前样式表的字符集,默认与当前网页相同)
* 3: CSSRule.IMPORT_RULE (定义一个CSSImportRule对象,即用@import引入其他样式表)
* 4: CSSRule.MEDIA_RULE (定义一个CSSMediaRule对象,用于设定此样式适用于显示器、打印机或投影仪等)
* 5: CSSRule.FONT_FACE_RULE (定义一个CSSFontFaceRule对象,对应CSS3的@font-face)
* 6: CSSRule.PAGE_RULE (定义一个CSSPageRule对象)
cssText属性无需多言,它是一个非常实用的属性,可以直接将字符串转换为样式规则,从而忽略各浏览器在样式属性命名上的差异(如cssFloat与styleFloat)。
parentStyleSheet和parentRule属性主要针对@import规则。不过,由于@import在IE中存在兼容性问题,我通常不使用它。
此外,还有几个生不逢时的方法:
* insertRule(rule, index):添加一条样式规则。
* deleteRule(index):移除一条样式规则。
* getPropertyValue(propertyName):获取元素相应样式属性的值。例如,获取一个样式规则对象后,可以利用CSSStyleRuleObject.getPropertyValue("color")来获取其字体颜色的设置。与普通el.style.color方法相比,其效率相当高,因为el.style.color获取的是内联样式。像IE这样的浏览器,如果元素没有设置style属性,根本无法得到准确的值,可能为空,可能是inherit……还可能涉及兼容性问题。而且,这些内联属性不一定是最终应用于元素的样式。在IE中,只有调用不那么“废柴”的el.currentStyle[prop],其他浏览器则调用相当“争气”但略显麻烦的document.defaultView.getComputedStyle(el, "")[prop]。
* removeProperty(propertyName):移除元素相应样式属性。
* setProperty(propertyName, value, priority):设定元素添加一个样式,还能指定优先级。
样式操作函数与兼容性问题
我们可以编写一个设定样式的函数:
代码如下:
var hyphenize = function(name) {
return name.replace(/([A-Z])/g, "-$1").toLowerCase();
}
var camelize = function(name) {
return name.replace(/\-(\w)/g, function(all, letter) {
return letter.toUpperCase();
});
}
var setStyle = function(el, styles) {
for (var property in styles) {
if (!styles.hasOwnProperty(property)) continue;
if (el.style.setProperty) {
//必须是连字符风格,el.style.setProperty('background-color','red',null);
el.style.setProperty(hyphenize(property), styles[property], null);
} else {
//必须是驼峰风格,如el.style.paddingLeft = "2em"
el.style[camelize(property)] = styles[property]
}
}
return true;
} 使用方法:
代码如下:
setStyle(div, {
'left': 0,
'top': 0,
'line-height': '2em',
'padding-right': '4px'
}); 不过,我非常不喜欢这种方法,因为它生成的是内联样式,还需要特殊处理float和opacity。在IE7的内联样式中,滤镜还有一个Bug,必须使其获得hasLayout,否则滤镜不会生效(我们可以通过el.currentStyle.hasLayout查看其状况)。因此,与其一个个设置,不如用cssText一网打尽。
增强版addSheet函数实现
最后附上我的增强版addSheet方法。它增加了自动处理opacity的功能,也就是说,我们只需按标准设置cssText,它会自动生成相应的滤镜,这样一来至少能让Firefox等浏览器通过W3C校验。
代码如下:
var addSheet = function() {
var doc, cssCode;
if (arguments.length == 1) {
doc = document;
cssCode = arguments[0]
} else if (arguments.length == 2) {
doc = arguments[0];
cssCode = arguments[1];
} else {
alert("addSheet函数最多接受两个参数!");
}
if (!+"\v1") { //新增功能,用户只需输入W3C的透明样式,它会自动转换成IE的透明滤镜
var t = cssCode.match(/opacity:(\d?\.\d+);/);
if (t != null) {
cssCode = cssCode.replace(t[0], "filter:alpha(opacity=" + parseFloat(t[1]) * 100 + ");");
}
}
cssCode = cssCode + "\n"; //增加末尾的换行符,方便在Firebug下的查看。
var headElement = doc.getElementsByTagName("head")[0];
var styleElements = headElement.getElementsByTagName("style");
if (styleElements.length == 0) { //如果不存在style元素则创建
if (doc.createStyleSheet) { //ie
doc.createStyleSheet();
} else {
var tempStyleElement = doc.createElement('style'); //w3c
tempStyleElement.setAttribute("type", "text/css");
headElement.appendChild(tempStyleElement);
}
}
var styleElement = styleElements[0];
var media = styleElement.getAttribute("media");
if (media != null && !/screen/.test(media.toLowerCase())) {
styleElement.setAttribute("media", "screen");
}
if (styleElement.styleSheet) { //ie
styleElement.styleSheet.cssText += cssCode; //添加新的内部样式
} else if (doc.getBoxObjectFor) {
styleElement.innerHTML += cssCode; //火狐支持直接innerHTML添加样式表字串
} else {
styleElement.appendChild(doc.createTextNode(cssCode))
}
} JS在setTimeout或Ajax回调中打开新标签页失败怎么处理
2026-11-05 15:50:12
JS直播协议选型:HLS、FLV与WebRTC的延迟与兼容性对比
2026-11-05 12:16:14
JS实现回到顶部:scrollTop赋值与浏览器兼容性处理
2026-11-04 23:43:35
JS使用classList动态添加、移除和切换CSS类名
2026-11-04 23:40:58
在 D3.js 中实现平滑过渡动画的技巧
2026-11-03 23:46:09
