本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。
JavaScript也可以针对CSS进行编程,也就是所谓的脚本化CSS;通过脚本化CSS,同样可以达到一系列的视觉效果;
在HTML中定义样式的方式有3种:通过元素包含外部样式表文件、使用
注:以上border属性可能不会返回实际的border规则(如IE和Firefox返回空字符串),原因是不同浏览中解释复合属性的方式不同,因为设置这种属性实际上会涉及很多其他属性,例如:border,实际上调协了四个边的边框宽度、颜色等,因此border不会返回,但
computedStyle.borderleftWidth会返回值;
console.log(computedStyle.borderLeftWidth);
console.log(computedStyle.borderLeftColor);
另外,不同浏览器表示值的方式可能会有所区别;
计算后的样式也包含属于浏览器内部样式表的样式信息,因此任何具有默认值的CSS属性都会表现在计算后的样式中;如visibility属性都有一个默认值,有些浏览器设置为visible,而有些设置为inherit;
计算样式的CSSStyleDeclaration对象和表示内联样式的对象之间有一些重要的区别:
计算样式的属性是只读的;
计算样式的值是绝对值,类似百分比和点之类的相对的单位将全部转换为绝对值;所有指定尺寸,例如外边距大小和字体大小等属性都有一个以像素为度量单位的值;相关颜色的属性将以”rgb(#,#,#)”或”rgba(#,#,#,#)”的格式返回;
不计算复合属性,它们只基于最基础的属性,例如,不要查询margin属性,应该使用marginLeft或marginTop等;
计算样式的cssText属性未定义(也就是该属性返回空字符串);
计算样式和内联样式可以同时使用;
// 用指定的因子缩放元素e的文本尺寸
function scale(e, factor){
// 用计算样式查询当前文本的尺寸
var size = parseInt(window.getComputedStyle(e,"").fontSize);
// 用内联样式来放大尺寸
e.style.fontSize = factor * size + "px";
}
// 用指定的因子修改元素的背景颜色
// factors > 1 颜色变浅,factors < 1颜色变暗
function scaleColor(e, factor){
var color = window.getComputedStyle(e,"").backgroundColor;
var components = color.match(/[d.]+/g); // 解析r、g、b分量
for(var i=0; i<3; i++){ // 循环r,g,b
var x = Number(components[i]) * factor; // 缩放每个值
x = Math.round(Math.min(Math.max(x, 0), 255)); // 设置边界并取整
components[i] = String(x);
}
if(components.length == 3) // rgb()颜色
e.style.backgroundColor = "rgb(" + components.join() + ")";
else // rgba()颜色
e.style.backgroundColor = "rgba(" + components.join() + ")";
}
var mydiv = document.getElementById("mydiv");
scale(mydiv, 1.5);
scaleColor(mydiv, .5);
低版本的IE不支持getComputedStyle()方法,但它有一种类似的概念;在IE中,具有style属性的元素还有一个currentStyle属性,该属性是CSSStyleDeclaration的实例,包含当前元素全部计算后的样式,但只有IE支持;
var computedStyle = mydiv.currentStyle;
console.log(computedStyle.backgroundColor);
console.log(computedStyle.width);
console.log(computedStyle.height);
console.log(computedStyle.borderLeftWidth);
兼容函数:
function getStyle(obj, attr){
if(window.getComputedStyle)
return getComputedStyle(obj, null)[attr];
else
return obj.currentStyle[attr];
}
var mydiv = document.getElementById("mydiv");
var backgroundColor = getStyle(mydiv, "background-color");
console.log(backgroundColor); // rgb(245, 222, 179)
// 或者
function getCss(obj, css){
return (document.defaultView.getComputedStyle ?
document.defaultView.getComputedStyle(obj,null) :
obj.currentStyle)[css];
}
var borderTopWidth = getCss(mydiv, "border-top-width");
console.log(borderTopWidth); // 1px
封装一下函数,用来获取CSS属性值,如:
function getComputedStyles(elem,prop) {
var cs = window.getComputedStyle(elem,null);
if (prop) {
console.log(prop+" : "+cs.getPropertyValue(prop));
return;
}
var len = cs.length;
for (var i=0;i
console.log(style+" : "+cs.getPropertyValue(style));
}
}
getComputedStyles(mydiv); // 查询所有
getComputedStyles(mydiv,"background-color"); // 只查询一个属性
与伪元素一起使用:getComputedStyle可以从伪元素中提取样式信息(例如:::after, ::before, ::marker, ::line-marker);
使用计算样式是可以获取元素的几何尺寸和位置的,但是其获得的结果并不一定是我们所期望的,此时可以使用getBoundingClientRect(),其返回的值是与呈现的效果是一致的;
console.log(computedStyle.left); // auto
console.log(computedStyle.top); // auto
console.log(computedStyle.width); // 300px
// left:8 top:8 width:302,包括了border
var rect = mydiv.getBoundingClientRect();
console.log(rect);
脚本化CSS类:
也可以脚本化元素的class属性,改变元素的class就改变了应用于元素的一组样式表选择器,它能在同一时刻改变多个CSS属性;
className属性:
与元素的class特性对应,即为元素指定的CSS类;由于class是ECMAScript保留字,所以在Javascript中使用className;
在操作类名时,需要通过className属性添加、删除和替换类名;
var mydiv = document.getElementById("mydiv");
mydiv.className = "container"; // 设置
mydiv.className = ""; // 删除
mydiv.className = "other"; // 替换
// 或
if(mydiv.className == ""){
mydiv.className = "container";
}
元素可以设置多个CSS类,其className中保存的是拥有多个类名的字符串,因此即使只修改字符串一部分,都会覆盖之前的值,所以每次都必须设置整个字符串的值;
var mydiv = document.getElementById("mydiv");
console.log(mydiv.className); // db user disable
mydiv.className = "container";
console.log(mydiv.className); // container
如果要从class中只删除一个类名,需要把多个类名拆开,删除不想要的那个,然后再把其他类名拼成一个新字符串,如:
// 如,删除user类
// 首先,取得类名字符串并拆分成数组
var mydiv = document.getElementById("mydiv");
var classNames = mydiv.className.split(/s+/);
// 找到要删的类名
var pos = -1, i, len;
for(i=0, len = classNames.length; i
pos = i;
break;
}
}
// 删除类名
classNames.splice(i,1);
// 把剩下的类名拼成字符串并重新设置
mydiv.className = classNames.join(" ");
如果要添加类名,是可以直接通过拼接字符串完成,但在拼接之前,必须要通过检测确定不会多次添加相同的类名;
Element.classList属性:
HTML5新增了一种操作类名的方式,可以让操作更简单也更安全,即classList属性,其是一个DOMTokenList对象,其是只读的类数组对象,与其他DOM集合类似,它也有一个表示自己包含多少个元素的length属性,而要取得每个元素可以使用item()方法,也可以使用方括号语法,此外,这个新类型还定义如下的方法:
- add(value):将给定的字符串值添加到列表中,如果值存在,就不添加;
- contains(value):表示列表中是否存在给定的值,如果存在返回true,否则,返回false;
- remove(value):从列表中删除给定的字符串;
- toggle(value):如果列表中已经存在给定的值,删除它,否则,添加它;
console.log(mydiv.classList); // DOMTokenList
mydiv.classList.add("container");
mydiv.classList.remove("container");
使用classList,可以确保其他类名不受此次修改的影响,可以极大地减少类似的基本操作的复杂性;
mydiv.classList.toggle("user"); // 切换user类
// 确定元素中是否包含既定的类名
if(mydiv.classList.contains("bd") && !mydiv.classList.contains("disabled")){
// To Do
}
// 迭代类名
for(var i=0,len=mydiv.classList.length; i
console.log(mydiv.classList[i]);
}
有了classList属性,除非需要全部删除所有类名,或者完全重写元素的class属性,否则也就用不到className了;
需要确定的一点,classList这个DOMTokenList对象,它是实时的,如果className属性改变了,它也会及时更新,同样的,classList改变了,className属性也会及时更新;
IE9以下的浏览器不支持classList属性,可以自定义一个CSSClassList类,模拟DOMTokenList对象的方法;
function classList(e){
// 以下两行先注释,否则后面的toArray默认调用的是DOMTokenList对象的的toArray,而它并不存在
// 或者扩展内置的DOMTokenList的toArray
// if(e.classList) return e.classList; // 如果e.classList存在,则返回它
// else return new CSSClassList(e); // 否则,说伪造一个
return new CSSClassList(e);
}
// CSSClassList是一个模拟DOMTokenList的对象
function CSSClassList(e) { this.e = e;}
// 如果e.className包含类名c则返回true,否则返回false
CSSClassList.prototype.contains = function(c){
// 检查c是否是合法的类名
if(c.length === 0 || c.indexOf(" ") != -1)
throw new Error("Invalid class name: '" + c + "'");
// 首先是常规检查
var classes = this.e.className;
if(!classes) return false; // e不含类名
if(classes === c) return true; // e有一个完全匹配的类名
// 否则,把c自身看做一个单词,利用正则表达式搜索c
return classes.search("b" + c + "b") != -1;
};
// 如果c不存在,将c添加到e.className中
CSSClassList.prototype.add = function(c){
if(this.contains(c)) return; // 如果存在,什么也不做
var classes = this.e.className;
if(classes && classes[classes.length - 1] != " ")
c = " " + c; // 如果需要加一个空格
this.e.className += c; // 将c添加到className中
};
// 将在e.className中出现的所有c都删除
CSSClassList.prototype.remove = function(c){
if(c.length === 0 || c.indexOf(" ") != -1)
throw new Error("Invalid class name: '" + c + "'");
// 将所有作为单词的c和多余的尾随空格全部删除
var pattern = new RegExp("b" + c + "bs*", "g");
this.e.className = this.e.className.replace(pattern, "");
};
// 如果c不存在,将c添加到e.className中,并返回true
// 否则,将e.className中出现的所有c都删除,并返回false
CSSClassList.prototype.toggle = function(c){
if(this.contains(c)){ // 如果e.className包含类名c
thsi.remove(); // 删除它
return false;
}else{
this.add(c); // 添加
return true;
}
};
// 返回e.className本身
CSSClassList.prototype.toString = function(){
return this.e.className;
};
// 返回在e.className中的类名
CSSClassList.prototype.toArray = function(){
return this.e.className.match(/bw+b/g) || [];
};
// 应用
var mydiv = document.getElementById("mydiv");
var ccl = classList(mydiv);
console.log(ccl);
console.log(ccl.contains("newsdiv")); // true
ccl.add("topDiv");
ccl.remove("newsdiv");
ccl.toggle("newsdiv");
console.log(ccl.toString());
console.log(ccl.toArray());
脚本化样式表:
在脚本化样式表时,会使用到两种类型的对象:
第一类是元素对象,包括通过元素包含的样式表和在