闭包与模仿块作用域

闭包与模仿块作用域

  最近看《JavaScript语言精粹》真是一页的含量顶其它书的十页啊,受益匪浅,在这里整理一下闭包的一些东东吧。

  首先我也忘了之前有没有整理过关于原型的一些用法,先给出一段代码:

1
2
3
4
Function.prototype.method=function(name,func){
this.prototype[name]=func;
return this;
}

  具体要是忘了的话,去看原型那节,作用是给Function对象的原型添加了一个method方法,用于往该原型里新增方法,这样继承自Function对象的子对象们都会动态更新它们的方法。

  然后就是我们主要整理的东西了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String.method("deentityify",function(){
var entity={
quot: '"',
lt: '<',
gt: '>'
};
return function(){
return this.replace(/&([^&;]+);$/g,function(a,b){
var r=entity[b];
return typeof r === "string" ? r : a;
});
};
}());
document.writeln("&lt;body&gt;&quot;HAHAHA&quot;&lt;body&gt;".deentityify())//<body>"HAHAHA"<body>

  这段代码的主要功能是:给String对象添加一个deentityify方法。它的任务是寻找字符串中的HTML字符实体并把它们替换为对应的字符。
  这里有四个需要注意的点:

1.模仿块作用域

1
2
3
(function(){
var s="test";
}())

  JS中函数可以用来构造函数作用域,在函数里面可以看到外面的变量,但在函数外面则无法看到函数里面的变量,这是因为函数中搜索变量的过程是由内到外沿着作用域链逐层搜索,一直搜索到全局对象为止。

  因为JS将function关键字当作一个函数声明的开始,而函数声明后面不能跟圆括号。然而,函数表达式的后面可以跟圆括号。所以最外层的把整个函数包起来的圆括号表示:将函数声明转换成函数表达式。

  这段代码实际上定义并立即调用了一个匿名函数,其中最后的()圆括号是调用运算符

  要注意()调用运算符立刻调用了我们刚刚构造出来的函数,这个调用创建并返回的函数才是deentityify()方法,
  即:

1
2
3
deentityify=function(){
return this.replace(...);
}

  在匿名函数中定义的任何变量,都会在执行结束时被销毁,这样就可以减少闭包占用的内存问题。

2.闭包

  一个函数可以访问它被创建时所处的上下文环境,这称为闭包。例子中第一个return的函数就是一个闭包:

1
2
3
4
5
6
return function(){
return this.replace(/&([^&;]+);$/g,function(a,b){
var r=entity[b];
return typeof r === "string" ? r : a;
});
};

  上面的函数可以访问到deentityify方法上下文环境定义的entity对象,所以这个函数就可以被称作闭包

  在这里再讨论一下变量的生存周期:

  全局变量的生存周期是永久,除非我们主动销毁(=null)这个全局变量。

  在函数内用var关键字声明的局部变量,当退出函数时,会随着函数调用的结束而被销毁。

  但是如果局部变量被封闭在闭包形成的环境中,那么这个局部变量就能一直生存下去。像上面的闭包,它可以访问到deentityify()被调用时产生的环境,所以局部变量entity因为能被外界访问,所以就不会被销毁,除非我们手动销毁。

  所以说闭包的作用有:

  1. 可以延长局部变量的生存周期。

  2. 可以封装变量

3.模块模式

  模块是一个提供接口却隐藏状态与实现的函数或对象。它利用函数作用域与闭包来创建被绑定对象与私有成员的关联,模块模式的一般形式是:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把它们保存到一个可访问到的地方。

4.String.prototype.replace()进阶用法

  我这也是第一次知道它的第二个参数可以是一个函数,且非常有用,具体见:MDN文档

指定一个函数作为参数

  你可以指定一个函数作为第二个参数。在这种情况下,当匹配执行后, 该函数就会执行。 函数的返回值作为替换字符串。 (注意:上面提到的特殊替换参数在这里不能被使用。) 另外要注意的是, 如果第一个参数是正则表达式, 并且其为全局匹配模式, 那么这个方法将被多次调用, 每次匹配都会被调用。

  下面是该函数的参数:

  变量名   代表的值
  match   匹配的子串。(对应于上述的$&。)
  p1,p2, … 假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)
  offset  匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串是“bc”,那么这个参数将是1)
  string  被匹配的原字符串。

  代码中的正则表达式/&([^&;]+);$/g代表:从整个字符串中,从全局匹配出所有的以&开头,中间有1个或多个非&;的任意字符,然后以;结尾的字符串。

函数柯里化

函数柯里化

  函数柯里化Currying指的是把函数与传递给它的参数相结合,产生出一个新的函数

  原理:Currying通过创建一个保存着原始函数(对象)和要被套用的参数的闭包来工作。它返回另一个函数,当函数被调用时,会返回调用原始函数(对象)的结果,并传递调用curry时的参数“加上”当前调用的参数。”加上”是通过Array.concat()方法连接两个参数数组。

  上面的原理balabala一大顿,我都没看懂,所以为了更容易懂,还是得看代码:

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.method=function(name,func){
this.prototype[name]=func;
return this;
}
Function.method("curry",function(){
var slice=Array.prototype.slice,
args=slice.apply(arguments),
that=this;
return function(){
return that.apply(null,args.concat(slice.apply(arguments)));
};
});

  然后接下来我们就可以看到它运用的结果

1
2
3
4
5
function add(num1,num2){
return num1+num2;
}
var add1=add.curry(1);
alert(add1(6)); //7

  这里的method方法中,要注意四点:

1.arguments对象保存着函数被调用时的参数列表,它是一个类数组对象,有一个length属性但没有任何数组的方法。

  第一个arguments是curry()函数的参数,return里的arguments是return function()的参数,这样可能听不懂,其实在例子中就是add1()函数里的参数。

2.正因为第一点,所以要想在最后使用args.concat,必须将arguments对象转换为一个真的数组,这里用Array.prototype.slice()方法,它可以将一个类数组(Array-like)对象/集合转换成一个新数组。

  具体见:MDN文档

3.第一个that=this;中的this在这里属于方法调用模式,绑定到调用该方法的对象上,即一个Function对象。

  所以add.curry(1),this被绑定到add函数对象上

4.调用函数的方法apply(要绑定给this的值,参数数组)

  具体可见博客中“JS中的apply()使用详解一节”。

  最后我们再结合代码来看curry方法的具体作用:

1
2
3
4
5
6
7
8
9
10
Function.method("curry",function(){
//curry方法通过创建一个保存着原始函数和要被套用的参数的闭包来工作。
var slice=Array.prototype.slice,
args=slice.apply(arguments), //要被套用的参数
that=this; //that=this保存着原始函数对象
return function(){ //闭包 && 它返回另一个函数
return that.apply(null,args.concat(slice.apply(arguments)));
//当函数被调用时,会返回调用原始函数的结果,并传递调用curry时的参数“加上”当前调用的参数
};
});

  所以当执行add1(6)时,相当于执行了add.apply(null,args.concat(slice.apply(arguments))),即执行了add(curry中的参数1,自己的参数6),所以放回1+6=7.

#####  这样就好理解柯里化的概念了:把函数与传递给它的参数相结合,产生出一个新的函数。

  add函数使用curry(参数)方法=>add.curry(参数);把函数与传递给它的参数 相结合,
  产生一个新的函数add1=>var add1=add.curry(1);
  用处就是可以用add1函数给原函数add传参,现阶段光是理论懂了,但是不懂具体用在哪,后期一定会再补充。

秒懂汉诺塔原理函数递归

秒懂汉诺塔原理函数递归

  想当年大一刚学C语言递归那一块儿的时候,遇到了“汉诺塔”问题,我的老天,那段时期想哭好吗,深刻的打击了我的编程热情,本身我就自觉智商不高,于是一直就没搞懂过这个问题,指导最近看了《JavaScript语言精粹》,才发现递归并没有那么难。。。我之前对于递归的了解只限于一个函数调用它自身,所以一直懵懵懂懂,现在需要重新认识一下它:

递归函数是干啥用的

  递归函数就是会直接或间接调用自身的一种函数。它把一个问题分解为一组相似的子问题,每一个都用一个寻常解去解决。也就是说,递归用一般的方式去解决每个子问题。

汉诺塔是什么

  因为这是篇笔记,相信每个想了解汉诺塔原理的人都懂它是什么,怎么玩,但是就算你会玩,你不会写代码(大佬除外)。。。我就是从这个时候觉得自己智商比别人低的,不过这是我刚接触编程的时候。到现在接触了两三年的编程,我深感周围大佬没多少(可能因为我双非一本非计算机专业的缘故),当初那些一看就会的人大部分接触的早O__O,人家初中高中奥数什么的早就接触啦,对于我们这种普通学生来说(我相信你要是985,211不会来看我的博客的),大部分像我这样的人(为了不让大佬们黑我)都是学以致用的,参考别人的摸出门路了才会用,所以接下来我写的,应该都能懂。

汉诺塔递归代码

  有时搜一篇资料,先看到源码,哇看不懂,再看原理,哇更不懂;也可能先看到原理,没代码我怎么会看懂,再看源码,哇原理我都看不懂看代码怎么会懂。。。所以这就陷入了死循环,至于能不能看懂,就看个人的喜好和悟性了,我的习惯是先看源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<pre>
<script type="text/javascript">
var hanoi=function(n,src,aux,dst){
if(disc>0){
//第一步
hanoi(n-1,src,dst,aux);
//第二步
document.writeln("Move disc "+n+" from "+src+" to "+dst);
//第三步
hanoi(n-1,aux,src,dst);
}
};
hanoi(3,"Src","Aux","Dst");
hanoi(4,"Src","Aux","Dst");
</script>
</pre>

汉诺塔递归原理

  你可以发现它看上去非常的简单,一共就三步,毕竟hanoi函数就是把一堆圆盘从一根柱子移到另一根柱子,必要时使用辅助的柱子。函数的参数解释如下:
1.n是一开始由小到大排在src柱子上的圆盘数量
2.src代表起始柱子
3.aux(auxiliary)代表辅助柱子
4.dst(destination)代表目标柱子

  hanoi函数把问题分解为了三个子问题,

第一步:它移动n-1个较小的圆盘到辅助柱子上,从而露出下面最大的圆盘。

第二步:移动下面最大的圆盘到目标柱子上。

第三步:将刚才较小的圆盘从辅助柱子上再移动到目标柱子上。

  然后通过递归地调用自身(只是参数不同)去处理一对圆盘的移动,从而解决那些子问题。

对递归原理的理解

  hanoi(3,”Src”,”Aux”,”Dst”);代表把3个圆盘Src柱子借助Aux柱子移动Dst柱子。每一个大问题里包含三个子问题:如果起始柱子上有盘子,先把disc-1个较小的圆盘都放到辅助柱子上,然后把较大的放到目标柱子上,最后在把那disc-1个较小的圆盘放到目标柱子上。

  相信读到这里的人都搞懂了怎么把这个大问题分解为3个子问题,并且发现如果按着原理思路来写,即可写出代码。但是过了不一会儿你就会产生疑问,因为你大问题上懂了,但不明白为啥每个子问题都可以这么用呢?每个子问题内为什么也起作用呢?

  所以说之前的我和现在的我差在了哪里,不是智商,而是思考的方式不对!!你是不是正在苦苦思考这段代码的每一步,然后把一步步的执行过程都写出来了,想搞懂递归呢?结果就是蛋疼!你不但啥也没搞懂,还白白浪费了这段推代码的时间!正确的打开方式是这样的——递归当然只能以递归的思路理解,把它展开纯属自讨苦吃

  递归思路,说白了是如下三步:

1.对于问题N,如果N-1已经解决了,那么N是否很容易解决。

  举例来说,如果要把一个N层汉诺塔从src搬到dst,那么:

  如果前N-1层可以找别人搞定,咱只管搬第N层,会不会变得非常容易?

  你看,这一下就简单了:这时当它只有两层就好了,先把前N-1层看作一个整体,把它搬到aux;然后把最下面的第N层搬到dst;然后再把前N-1层从aux搬到dst。

  类似的,假如接到“搬前N-1层”这个任务的是我们,怎么搬呢?

  简单,像前东家一样,把前N-2层外包出去,我们只搬第N-1层——其实和前面讨论过的“外包N-1层,只搬第N层”完全一样嘛。

  依此类推,一层层“外包”下去——我不管你们有多伤脑筋,反正只要你们把我外包给你的活干了,我就能干了我的活!

  这一步就是*递推

注意这里的搬法:搬第N层,就需要把前N-1层搬两次(起始到辅助,辅助再到目标),另外再把第N层搬一次(起始到目标);
搬第N-1层,又需要把前N-2层搬两次,然后再把N-1层搬一次,依此类推。
an=2*a(n-1)+1
a(n-1)=2*a(n-2)+1
……
a2=2*a1+1
a1=1
很容易知道,an需要搬2^n-1次。

2、一步步递推下去,终究会有个“包工头”,接到“搬第一层”的任务。

  第一层怎么搬?

  太简单了,让搬哪搬哪。

  换句话说,到此,递推就到了极限,简单粗暴直接做就可以了。

3.既然第一层搬了,那么第二层当然就可以搬了;第二层搬了,第三层又可以搬了……依次类推,直到第N层。于是问题搞定。

  这一步就是回归

  如上三步加起来,就是递归

  推而广之,任何问题,不管规模为N时有多复杂,只要把N-1那块“外包”给别人做之后,我们在这个基础上可以轻易完成N,那么它很可能就适合用“递归”解决。

  那么,怎么最终确定它能不能用“递归”做呢?

看当N取1或2之类最简情况时,问题是否可以解决——然后写程序解决它。

  容易看出,“递归”其实和“数学归纳法”的思路非常像:证明N=1时成立;证明若N=n-1成立,则N=n时也成立;如上两步得证,则命题在n>1时一定成立(n为自然数)。你看,我们没必要从1开始逐一验证每个自然数,只要证明了“基础条件”、再证明了“递推条件”,大自然的规律会帮我们搞定一切。

  换句话说,只要我们:

1、写程序告诉电脑“如何分解一个问题”(即把汉诺塔问题分解为如上三步)
2、写程序告诉电脑“当该问题分解到最简时如何处理”(即第二步中的直接把它从src移到dst)

  那么,“具体如何递推、如何回归”这个简单问题就不要再操心了,电脑自己能搞定。

  ——写出问题分解方法、写出分解到最简后如何解决,这是我们的任务;把问题搞定,是电脑的任务。这就是递归的魅力。

  正是由于这种“我提供思路你搞定细节”的特点,“一切皆递归”的函数系语言才被称为“声明式编程”(而不是必须一步一步指导电脑如何做的“命令式编程”)。

更多的关于汉诺塔递归问题可以参考

  

  

JS中的取整

JS中的取整

Math.ceil()——>天花板,向上取整

  -9 ——> 0 ——> 9,沿着这个方向无论正负都向上取整。

Math.floor()——>地板,向下取整

  -9 <—— 0 <—— 9,沿着这个方向无论正负都向下取整。

如何像其它语言那样向零取整

  先给Function.prototype添加method()方法使得该方法对所有函数可用:

1
2
3
4
Function.prototype.method=function(name,func){
this.prototype[name]=func;//方法调用模式,this绑定到该对象,即Function,再更新Function对象新增新的name属性
return this;
}

  然后给Number.prototype添加向零取整方法interger(),具体如下:

1
2
3
4
Number.method("integer",function(){
return Math[this<0 ? "ceil" : "floor"](this);
});
alert((-5.9).integer());

Math.round()——>把一个数字四舍五入为最接近的整数

  正数很正常:5.4——>5 , 5.5——>6
  负数有点小注意:-5.4——>-5, -5.5——>-5 , -5.51——>-6

阻止事件的一些操作

阻止事件冒泡或捕获

  有时会遇到如下情况,本元素和父层元素都有事件,但我们只想要本元素事件激活,即要阻止事件冒泡
HTML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>

</head>
<body>
<button onclick="clickBtn(event)">Push Me</button>
</body>
<script type="text/javascript">
document.onclick=function(){
alert("body");
}
function clickBtn(event){
event= event ? event : window.event;
alert("button");
event.stopPropagation();
//event.cancelBubble=true;
}
</script>
</html>

cancelBubble

  cancelBubble原本是IE独有,经测试发现它也可以在Chrome和Firefox中阻止冒泡,那是因为人家Chrome,Firefox 考虑周到,提供了这么一种可选的方式。 但是,cancelBubble已经不在标准中了,相信迟早它们会移除这一特性的。

DOM的stopPropagation()方法

  这是DOM层次的方法,用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。两者的区别为:
  IE不支持事件捕获,因而只能取消事件冒泡,但stopPropagation()可以同时取消事件捕获和冒泡。

阻止事件默认行为

  下面是一个阻止右键菜单栏事件的小程序

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
document.oncontextmenu=function(event){
event= event ? event : window.event;
/*下面这三种方法都可以*/
//event.preventDefault();
//event.returnValue=false;
return false;
}
</script>

returnValue=false

  也是原本IE的方法,它也可以在它也可以在Chrome和Firefox中阻止事件默认行为。

DOM的preventDefault()方法

  简单直观,直接阻止默认的行为,可以用在多个情况。

变量提升

变量提升

  最近发现了一个小问题:

1
2
3
4
5
6
7
var x="global value";
function getValue(){
alert(x);//undefined
var x="local value";
alert(x);//local value
}
getValue();

  我们可以发现第一个alert(x)结果为undefined,这就跟JS的变量提升有关系了。
  var关键字声明变量,无论声明在何处,都会被视为声明在函数的最顶部(不在函数内即在全局作用域的最顶部),这就是函数的变量提升。所以上面的代码实际上是:

1
2
3
4
5
6
7
8
var x="global value";
function getValue(){
var x; //普通的变量初始化就是undefined,所以就好理解了
alert(x);
var x="local value";
alert(x);
}
getValue();

  
  下面我们就来整理一下变量提升:

1.我是变量声明,我会被提升在作用域顶端!

1
var a;

2.我是变量定义,我的声明部分会被提升,赋值部分不会被提升!

1
var b="test";

3.我是函数定义,或者叫我函数表达式。其实我就是变量定义,只不过恰好被赋值的类型是函数,所以也只提升变量名(即提升声明部分),不提升函数值!

1
2
3
var c=function(){
console.log("test");
}

4.我是函数声明,所以我全部被提升了,包括函数名和函数体。另外,我的优先级比变量声明要高,名字和我相同的变量声明会被忽略!

1
2
3
function d(){
console.log("test");
}

  最后再补充一点,因为JS虽然有函数作用域,但没有块作用域,即函数中的参数和变量在函数外部是不可见的,而在一个函数内部任何位置定义的变量,在该函数内部任何地方都可见。

offsetLeft和style.left

offsetLeft和style.left

offsetLeft

  1.如果父辈元素中有定位的元素,那么就返回距离当前元素最近的定位元素边缘的距离。
  2.如果父辈元素中没有定位元素,那么就返回相对于body左边缘距离。

两者的区别

  1. style.left 返回的是字符串,如28px,offsetLeft返回的是数值28,如果需要对取得的值进行计算,还用offsetLeft比较方便。
  2. style.left是读写的,offsetLeft是只读的,所以要改变元素的位置,只能修改style.left。
  3. style.left的值需要事先定义,否则取到的值为空。而且必须要定义在html里,offsetLeft的值随时可以取到。

JS清空数组的方法

①splice

1
2
var ary = [1,2,3,4]; 
ary.splice(0); //不用写要删的长度,直接从0全删掉

②length=0

1
2
var ary = [1,2,3,4]; 
ary.length=0;

  这种方式很有意思,其它语言如Java,其数组的length是只读的,不能被赋值。而JS中则可以。

③赋值为[]

1
2
var ary = [1,2,3,4]; 
ary = []; // 赋值为一个空数组以达到清空原数组

  方式2 保留了数组其它属性,方式3 则未保留。很多人认为方式2的效率很高些,因为仅仅是给length重新赋值了,而方式3则重新建立个对象。经 测试 恰恰是方式3的效率高。测试代码:

1
2
3
4
5
6
7
8
var a=[];
for(var i=0;i<1000000;i++){
a.push(i);
}
var start=new Date();
a=[];
var end=new Date();
alert(end-start);

  可以多试几个浏览器,结果是方式3更快一点点。

内联元素和块元素

内联元素和块元素

①块级元素:默认100%撑满外层盒子的宽度(浏览器显示区域),独占一行,垂直排列,里能用行内元素,可以设置宽高

  |–>div 没有任何CSS属性(主要用在大框架)
  |–>p 自带上下外边据 (用在段落)
  |–>h1-h6 自带上下外边据,自带字体大小(用在栏目大标题)
  |–>ul li 自带上下外边据,li自带圆点 (用在列表,多个元素大小一致)
  |–>dl dt dd 用在带标题的列表上

②行内元素(内联元素):默认宽度只有本身所占的宽度,默认水平排列,不支持宽高 ,margin padding左右有效果 上下没有效果

  |–>a 超链接 自带标签颜色、鼠标放上颜色,点击时的颜色,点击过颜色,下划线
  |–> span
  |–>em、i(默认斜体)
  |–>strong,b(默认加粗)
  |–>u(默认下划线)

③行内块级元素:默认宽度只有本身所占的宽度,默认水平排列,支持宽高,受到父级text-align影响,可以居中

  |–>img 图片
  |–>input 输入框
  |–>select 下拉菜单
  |–>textarea 输入区域

IE6—————-BUG———————-
块级元素转化成行内块级元素 IE6不支持
行内元素转化成行内块级元素 IE6支持

块级元素加上position:absolute fixed(定位)或者浮动 宽度不是100% 是本身内容的宽度
行内元素加上position:absolute fixed(定位)或者浮动 就会变成块级元素

display:block; display:inline;和display:inline-block;

block:使用此属性后,元素会被显示为块级元素,元素会进行换行,默认宽度为撑满外层盒子的宽度(因为块元素霸道)。

inline:使用此属性后,元素会被显示为内联元素,元素则不会换行,默认宽度为内容的宽度。

inline-block:是使元素以块级元素的形式呈现在行内。意思就是说,让这个元素显示在同一行不换行,但是又可以控制高度和宽度,这相当于行内元素的增强。

  应用:
  1.display:inline-block把一个元素变成块级元素,但他的大小只是他本身内容的大小并不撑满外围盒子,如下图所示:
  内联元素与块级元素的关系所以在一道练习中出现下面的情况:

1
<li><a href="javascript:;">手写</a></li>

  若不把a变成块元素,则a上的背景不会撑满整个li。

  2.li是块元素,原本会换行,是竖着一列的布局,但改为inline-block并在父元素指定了width后,则会以块级元素的形式呈现在行内,不换行,但能控制高度宽度,这样就可以变成一行行排列的布局了。

css属性

  | ———-①盒子模型:width、height,padding、border、margin
   注意事项:padding 上下左右 全部相加 margin 上下取最大 左右相加 margin-top如想父级元素不受影响,父级元素要加overflow:hidden
  | ———-②-1 背景相关:background背景缩写:background-color,background-image,background-repeat,background-attachment,background-position
  | ———-②-2 文本样式:font-size文字大小/line-height行高、color文本颜色、text-align水平位置、font-family文字字体、letter-spcing文字间隔、text-decoration文本样式
  | ———-②-3 UL li:list-style列表样式
  | ———-②-4 A链接:a{}
    a:link - 普通的、未被访问的链接
    a:visited - 用户已访问的链接
    a:hover - 鼠标指针位于链接的上方
    a:active - 链接被点击的时刻
  | ———-③选择器 style 优先级最高 | ID选择器 #xxx id=”xxx” | 类选择器 .xxx class=”xxx” | 元素选择器 p | 后代选择器 .xxx .yyy
  | ———-④定位 float(浮动) position(定位):relative(相对)absolute(绝对)fixed(相对浏览器固定)

JS中style,currentStyle和getComputedStyle的区别以及获取css操作方法

JS中style,currentStyle和getComputedStyle的区别以及获取css操作方法

  首先需要了解CSS的三种使用方法:

1.行内样式

1
2
3
4
5
6
7
8
9
10
11
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>css行内样式</title>
</head>
<body>
<div style="width:100px;height:100px;background:red;"></div>

</body>
</html>

2.内嵌样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>css内嵌样式</title>
</head>
<body>
<style type="text/css">
#div{width:100px;height:100px;background:red;}
</style>
<div id="div"></div>

</body>
</html>

3.外部样式

1
2
3
4
5
6
7
8
9
10
11
12
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>css内嵌样式</title>
<link rel="stylesheet" type="text/css" href="XX.css">
</head>
<body>
<div id="div"></div>

</body>
</html>

  最近做了个小练习,点击h2想让ul元素在隐藏和显示中切换,但发现第一次点击总是无效,必须点到第二下以后才有想要的效果

HTML:

1
2
3
4
5
6
7
8
9
<h2>播放列表...</h2>
<ul>
<li><a href="javascript:;">玩家之徒 - 蔡依林</a></li>
<li><a href="javascript:;">原谅我就是这样的女生 - 戴佩妮</a></li>
<li><a href="javascript:;">猜不透 - 丁当</a></li>
<li><a href="javascript:;">自导自演 - 周杰伦</a></li>
<li><a href="javascript:;">浪漫窝 - 弦子</a></li>
<li><a href="javascript:;">流年 - 王菲</a></li>
</ul>

JS:

1
2
3
4
5
var oUl=document.getElementsByTagName("UL")[0];
var oH2=document.getElementsByTagName("H2")[0];
oH2.onclick=function(event){
oUl.style.display=oUl.style.display=="block"?"none":"block";
}

  原因为我以为ul默认为display:block; JS中的oUl.style.display能获取到block这个样式,但其实不然,js的style属性只能获取行内样式,所以一开始oUl.style.display=undefined,自然就不会第一下点击就隐藏了。

  解决方法是:在行内直接写入样式

1
2
3
<ul style="display: block;">
...
</ul>

  这样就能获取到了,所以在这里做一个总结:

1.style:各大浏览器都兼容,能设置样式和获取样式,但是获取不了嵌入和外部样式,如果写了行内没有的样式,返回的是空值

  写法:ele.style.attr(获取),ele.style.attr=”值”(设置);

  注意只是获取不了嵌入和外部样式,但可以设置样式。

currentStyle属性和getComputedStyle属性不能设置属性,只能获取

2.currentStyle:该属性只兼容IE

  Element.currentStyle 是一个与 window.getComputedStyle方法功能相同的属性。这个属性实现在旧版本的IE浏览器中.

  写法:ele.currentStyle[“attr”]或者ele.currentStyle.attr;

3.getComputedStyle:该属性是兼容火狐谷歌,不兼容IE9.0以下版本

  getComputeStyle返回的样式是一个实时的 CSSStyleDeclaration 对象,当元素的样式更改时,它会自动更新本身。详情见MDN文档

  写法:window.getComputedStyle(ele,null)[attr]获取是window.getComputedStyle(ele,null).attr

  关于它们的区别可以参考:获取元素CSS值之getComputedStyle方法熟悉  

  最后附上一段封装的获取CSS的代码

JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//设置或读取CSS样式
function css(obj,attr,value){
switch(arguments.length){
case 2://如果有两个参数可能是设置或读取样式
if(typeof arguments[1] == "object"){//即attr是个数组对象就设置样式
for(var i in attr){
i == "opacity" ? (obj.style["filter"]="alpha(opacity="+attr[i]+")",
obj.style[i]=attr[i]/100) : obj.style[i]=attr[i];
}
}else{//attr是个字符串就读取CSS样式
return obj.currentStyle ? obj.currentStyle[attr] : getComputeStyle(obj,null)[attr]
}
break;
case 3://三个参数就是设置样式
attr == "opacity" ? (obj.style["filter"]="alpha(opacity="+value+")",
obj.style[attr]=value/100) : obj.style[attr]=value;
break;
}
}
,