孙振的博客

Record my life , my study , my think


  • 首页

  • 标签

  • 时间线

兼容绑定事件的3种方法

发表于 2017-04-12

直接绑定(不利于UI和代码分离,event详见后面)

1
<input type="button" onclick="doEventThing(event)">

DOM事件句柄

1
document.getElementById("divid").onclick = function(){};

侦听函数

1
2
3
4
5
6
7
8
9
var tb = document.getelementbyid("tab1");
if(window.event==null){// 只有ie下,event是window的对象属性,注意两个侦听函数的区别.该方法只能在没定义event才行
td.attachEvent('onclick', function(){alert('21');});
td.attachEvent('onclick', function(){alert('21');})
}
else { // mozilla, netscape, firefox,w3c
td.addEventListener('click', alert('11'), false);
td.addEventListener('click', alert('12'), false);
}

关于变量类型的判断

发表于 2017-04-12

在JavaScript中,有5种基本数据类型和1种复杂数据类型,基本数据类型有:Undefined, Null, Boolean, Number和String;复杂数据类型是Object,Object中还细分了很多具体的类型,比如:Array, Function, Date等等。今天我们就来探讨一下,使用什么方法判断一个出一个变量的类型。

在讲解各种方法之前,我们首先定义出几个测试变量,看看后面的方法究竟能把变量的类型解析成什么样子,以下几个变量差不多包含了我们在实际编码中常用的类型。

1
2
3
4
5
6
7
8
9
10
11
var num = 123;
var str = 'abcdef';
var bool = true;
var arr = [1, 2, 3, 4];
var json = {name:'wenzi', age:25};
var func = function(){ console.log('this is function'); }
var und = undefined;
var nul = null;
var date = new Date();
var reg = /^[a-zA-Z]{5,20}$/;
var error= new Error();

使用typeof检测

我们平时用的最多的就是用typeof检测变量类型了。这次,我们也使用typeof检测变量的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(
typeof num,
typeof str,
typeof bool,
typeof arr,
typeof json,
typeof func,
typeof und,
typeof nul,
typeof date,
typeof reg,
typeof error
);
// number string boolean object object function undefined object object object object

从输出的结果来看,arr, json, nul, date, reg, error 全部被检测为object类型,其他的变量能够被正确检测出来。当需要变量是否是number, string, boolean, function, undefined, json类型时,可以使用typeof进行判断。其他变量是判断不出类型的,包括null。

还有,typeof是区分不出array和json类型的。因为使用typeof这个变量时,array和json类型输出的都是object。

使用instance检测

在 JavaScript 中,判断一个变量的类型常常会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 “object”。ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。例如:

1
2
3
4
5
function Person(){
}
var Tom = new Person();
console.log(Tom instanceof Person); // true

我们再看看下面的例子:

1
2
3
4
5
6
7
8
9
10
function Person(){
}
function Student(){
}
Student.prototype = new Person();
var John = new Student();
console.log(John instanceof Student); // true
console.log(John instancdof Person); // true

instanceof还能检测出多层继承的关系。

好了,我们来使用instanceof检测上面的那些变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
console.log(
num instanceof Number,
str instanceof String,
bool instanceof Boolean,
arr instanceof Array,
json instanceof Object,
func instanceof Function,
und instanceof Object,
nul instanceof Object,
date instanceof Date,
reg instanceof RegExp,
error instanceof Error
)
// num : false
// str : false
// bool : false
// arr : true
// json : true
// func : true
// und : false
// nul : false
// date : true
// reg : true
// error : true

从上面的运行结果我们可以看到,num, str和bool没有检测出他的类型,但是我们使用下面的方式创建num,是可以检测出类型的:

1
2
3
var num = new Number(123);
var str = new String('abcdef');
var boolean = new Boolean(true);

同时,我们也要看到,und和nul是检测的Object类型,才输出的true,因为js中没有Undefined和Null的这种全局类型,他们und和nul都属于Object类型,因此输出了true。

使用constructor检测

在使用instanceof检测变量类型时,我们是检测不到number, ‘string’, bool的类型的。因此,我们需要换一种方式来解决这个问题。

constructor本来是原型对象上的属性,指向构造函数。但是根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的。

我们先来输出一下num.constructor的内容,即数字类型的变量的构造函数是什么样子的:

1
function Number() { [native code] }

我们可以看到它指向了Number的构造函数,因此,我们可以使用num.constructor==Number来判断num是不是Number类型的,其他的变量也类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Person(){
}
var Tom = new Person();
// undefined和null没有constructor属性
console.log(
Tom.constructor==Person,
num.constructor==Number,
str.constructor==String,
bool.constructor==Boolean,
arr.constructor==Array,
json.constructor==Object,
func.constructor==Function,
date.constructor==Date,
reg.constructor==RegExp,
error.constructor==Error
);
// 所有结果均为true

从输出的结果我们可以看出,除了undefined和null,其他类型的变量均能使用constructor判断出类型。

不过使用constructor也不是保险的,因为constructor属性是可以被修改的,会导致检测出的结果不正确,例如:

1
2
3
4
5
6
7
8
9
10
function Person(){
}
function Student(){
}
Student.prototype = new Person();
var John = new Student();
console.log(John.constructor==Student); // false
console.log(John.constructor==Person); // true

在上面的例子中,Student原型中的constructor被修改为指向到Person,导致检测不出实例对象John真实的构造函数。

同时,使用instaceof和construcor,被判断的array必须是在当前页面声明的!比如,一个页面(父页面)有一个框架,框架中引用了一个页面(子页面),在子页面中声明了一个array,并将其赋值给父页面的一个变量,这时判断该变量,Array == object.constructor;会返回false;
原因:
1、array属于引用型数据,在传递过程中,仅仅是引用地址的传递。
2、每个页面的Array原生对象所引用的地址是不一样的,在子页面声明的array,所对应的构造函数,是子页面的Array对象;父页面来进行判断,使用的Array并不等于子页面的Array;切记,不然很难跟踪问题!

使用Object.prototype.toString.call

我们先不管这个是什么,先来看看他是怎么检测变量类型的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log(
Object.prototype.toString.call(num),
Object.prototype.toString.call(str),
Object.prototype.toString.call(bool),
Object.prototype.toString.call(arr),
Object.prototype.toString.call(json),
Object.prototype.toString.call(func),
Object.prototype.toString.call(und),
Object.prototype.toString.call(nul),
Object.prototype.toString.call(date),
Object.prototype.toString.call(reg),
Object.prototype.toString.call(error)
);
// '[object Number]' '[object String]' '[object Boolean]' '[object Array]' '[object Object]'
// '[object Function]' '[object Undefined]' '[object Null]' '[object Date]' '[object RegExp]' '[object Error]'

从输出的结果来看,Object.prototype.toString.call(变量)输出的是一个字符串,字符串里有一个数组,第一个参数是Object,第二个参数就是这个变量的类型,而且,所有变量的类型都检测出来了,我们只需要取出第二个参数即可。或者可以使用Object.prototype.toString.call(arr)==”object Array”来检测变量arr是不是数组。

jquery中$.type的实现

在jquery中提供了一个$.type的接口,来让我们检测变量的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(
$.type(num),
$.type(str),
$.type(bool),
$.type(arr),
$.type(json),
$.type(func),
$.type(und),
$.type(nul),
$.type(date),
$.type(reg),
$.type(error)
);
// number string boolean array object function undefined null date regexp error

看到输出结果,有没有一种熟悉的感觉?对,他就是上面使用Object.prototype.toString.call(变量)输出的结果的第二个参数呀。

常见的原生javascript DOM操作

发表于 2017-04-11

创建元素

创建元素:document.createElement()

使用document.createElement()可以创建新元素。这个方法只接受一个参数,即要创建元素的标签名。这个标签名在HTML文档中不区分大小写,在XHTML中区分大小写。

1
var div = document.createElement("div");

使用createElement()方法创建新元素的同时,也为新元素设置了ownerDocument属性,可以操作元素的特性。

1
2
div.id = "myDiv";
div.className = "div1";

此时,新元素尚未被添加到文档树中,因此设置各种特性均不会影响浏览器的显示。要添加到文档树,可用appendChild()、insertBefore()、replaceChild()。(稍后讲到)

1
document.body.appendChild(div);

当把元素添加到文档树中后,这个元素做的任何修改都会实时地反应到浏览器中。

在IE中可以为createElement()方法传入完整的元素标签和属性。(只在IE中兼容)

1
2
3
var div = document.createElement("<div id=\"mydiv\" class=\"div1\"></div>");
//不能再标签里加其他元素节点或者文本节点,如下的方式和上面的得出的节点一样
var div = document.createElement("<div id=\"mydiv\" class=\"div1\">12212</div>");

创建文本节点 :document.createTextNode

使用document.createTextNode()来创建文本节点,这个方法接受一个参数:要插入节点的文本。与设置已有文本节点的值一样,作为参数的文本将按照HTML或XML的格式进行编码。

1
document.createTextNode("121212");

可以添加多个文本节点。假如两个文本节点时相邻的同胞节点,那么两个文本节点会连起来,中间不会有空格。

节点关系

文本关系如下:

1
2
3
4
5
<div id="div1">
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
</div>

父节点:parentNode

parentNode是指定节点的父节点.一个元素节点的父节点可能是一个元素(Element )节点,也可能是一个文档(Document )节点,或者是个文档碎片(DocumentFragment)节点.
每一个节点都有一个parentNode属性。

对于下面的节点类型: Attr, Document, DocumentFragment, Entity, Notation,其parentNode属性返回null。如果当前节点刚刚被建立,还没有被插入到DOM树中,则该节点的parentNode属性也返回null。

1
2
3
4
<script type="text/javascript">
var child2 = document.getElementById("div2");
var parent = child2.parentNode;
</script>

子节点:childNodes

childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合(live collection)。
即时更新就是对节点元素的任意修改都会立即反映到结果里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script type="text/javascript">
var child2 = document.getElementById("div2");
var parent = child2.parentNode;
var allChilds = parent.childNodes;
/**
在IE6下才会返回3
在其他浏览器中会返回3个div节点+3个div节点的文本节点+1个自己的文本节点=7
下同
**/
console.log(allChilds.length) // IE下是3,其他浏览器是7
var nodeAdd = document.createElement("div");
var textAdd = document.createTextNode("这是添加的文本节点");
nodeAdd.appendChild(textAdd);
parent.appendChild(nodeAdd);
console.log(allChilds.length);// IE下是4,其他浏览器是8
</script>

兄弟节点:nextSibling,previousSibling

nextSibling返回某节点的下一个兄弟节点,previousSibling返回某节点的上一个兄弟节点,没有的话返回null。
注意:可能因为元素换行的原因返回的是text节点。

1
2
3
4
5
6
7
<script type="text/javascript">
var child3 = document.getElementById("div3");
var next = child3.nextSibling;
var previous = child3.previousSibling;
console.log(next); // IE下返回div4,其他返回text
console.log(previous) // IE下返回div2,其他返回text
</script>

第一个或最后一个子节点:firstChild、lastChild

firstChild返回node的子节点中的第一个节点的引用,没有返回null
lastChild返回node的子节点中的最后一个节点的引用,没有返回null

1
2
3
4
5
6
<script type="text/javascript">
var child3 = document.getElementById("div3");
var parent = child3.parentNode;
var first = parent.firstChild; // IE是div2,其他是text
var last = parent.lastChild; // IE是div4,其他是text
</script>

节点元素关系

只算元素,不算文本节点。

以下三个方法用法和节点关系完全一样,只是这三个方法只看元素节点,不管因为空格、换行造成的文本节点或者手动加上去的文本节点。
children: 返回所有元素子节点(IE5+、ff3.5、opera3、chrome,但在IE8及以下会将注释节点看成一个元素节点)

以下两个IE9+才支持
nextElementSibling:返回元素的下一个兄弟元素节点
previousElementSibling: 返回元素的上一个兄弟元素节点

节点元素关系

appendChild()

appendChild()用于向childNodes列表的末尾添加一个节点,并且返回这个新增的节点。
如果传入到appendChild()里的节点已经是文档的一部分了,那结果就是将节点从原来的位置转移到新位置,任何一个节点不能同时出现在文档中的多个位置。

1
2
3
var returnNode = someNode.appendChild(someNode.firstChild); // 返回第一个节点
console.log(returnNode === someNode.firstChild); // false
console.log(returnNode === someNode.lastChild); // true

insetBefore()

insetBefore()可以将节点插入到某个特定的位置。这个方法接受两个参数:要插入的节点和作为参照的节点。
插入节点后,被插入的节点变成参照节点的前一个同胞节点,同时被方法返回。 如果参照节点是null,则与appendChild()执行相同的操作。

1
2
3
4
5
6
7
8
9
10
11
12
// 插入后成为最后一个子节点
var returnNode = someNode.insetBefore(newNode, null);
console.log(returnNode === someNode.lastChild); // true
// 插入后成为第一个子节点
var returnNode = someNode.insetBefore(newNode, someNode.firstChild);
console.log(returnNode === newNode); // true
console.log(returnNode === someNode.firstChild); // true
// 插入到最后一个子节点的前面
var returnNode = someNode.insetBefore(newNode, someNode.lastChild);
console.log(returnNode === someNode.childNodes[someNode.childnodes.length - 2]) // true

替换节点: replaceChild()

replaceChild()接受两个参数:要插入的节点和要被替换的节点。被替换的节点将由这个方法返回并从文档中被移除,同时由要插入的节点占据其位置。

1
2
// 替换第一个子节点
var returnNode = someNode.replaceChild(newNode, someNode.firstChild);

使用replaceChild()后,被替换的节点的所有关系指针都会被复制到插入的节点上面。

删除节点:removeChild()

该方法移除节点,接受一个参数,即要移除的节点,同时该方法返回被移除的节点。只能是一个节点,不能是一组节点。

1
2
// 移除第一个子节点
var returnNode = someNode.removeChild(newNode, someNode.firstChild);

克隆节点:cloneNode(true/false)

返回调用该方法的节点的一个副本。参数表示是否采用深度克隆,如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身,文本或者换行、空格这些不会复制,因为他们都是一个textNode。

注意: 在DOM4规范中(实现于Gecko 13.0(Firefox 13.0 / Thunderbird 13.0 / SeaMonkey 2.10) , 查看 bug 698391),deep是一个可选参数. 如果省略的话, deep参数的默认值为true,也就是说,深度克隆是默认的.如果想使用浅克隆, 你需要将该参数指定为false。

在旧版本的浏览器中, 你始终需要指定deep参数。

克隆一个元素节点会拷贝它所有的属性以及属性值,当然也就包括了属性上绑定的事件(比如onclick=”alert(1)”),但不会拷贝那些使用addEventListener()方法或者node.onclick = fn这种用JavaScript动态绑定的事件。

1
2
3
var div1 = document.getElementById("div1");
var cloneHtml = div1.cloneNode(true);
document.body.appendChild(cloneHtml);

元素选择

HTML代码示例:

1
2
3
4
5
<div id="div1">
<p id="div2" class="one" name="nameone">2</p>
<div id="div3">3</div>
<div id="div4" name="div2">4</div>
</div>

querySelector、querySelectorAll(IE8及以上)

Selectors API通过匹配一组选择器的方式来为从DOM中检索Element节点提供一些简单快捷的方法,这比过去必须要在javascript代码中用循环来查找某个你想要的特定元素更快一些。
该规范对于使用Document,DocumentFragment和Element接口的对象都增了两种新方法:

querySelector
返回节点子树内与之相匹配的第一个Element节点。如果没有匹配的节点,则返回null。

querySelectorAll
返回一个包含节点子树内所有与之相匹配的Element节点列表,如果没有相匹配的,则返回一个空节点列表。

注意:由 querySelector()、querySelectorAll()返回的节点列表不是动态实时的(非live Collection)。这和其他DOM查询方法返回动态实时节点列表不一样。

选择器方法接受一个或多个用逗号分隔的选择器来确定需要被返回的元素。例如,要选择文档中所有CSS的类(class)是warning或者note的段落(p)元素,可以这样写:

1
var special = document.querySelectorAll( "p.warning, p.note" );

也可以通过ID来查询,例如:

1
var el = document.querySelector( "#main, #basic, #exclamation" );

执行上面的代码后,el就包含了文档中元素的ID是main,basic或exclamation的所有元素中的第一个元素。

querySelector() and querySelectorAll() 里可以使用任何CSS选择器,他们都不是live Collection:

1
2
3
4
5
var notLive = document.querySelectorAll("p");
console.log(notLive);
document.getElementById("div1").removeChild(document.getElementById("div2"));
console.log(notLive);
// 上面两个输出都是输出 `p#div2.one`的引用,没有因为删除了`p`标签而使`notLive`的结果发生变化。

getElementById()

返回一个匹配特定 ID的元素。id是大小写敏感的字符串,代表了所要查找的元素的唯一ID,如果没有则返回null。
如果新建一个元素,还没有插入到文档中,则不能通过该方法获取到。

1
2
3
4
5
var notLive = document.getElementById("div2");
console.log(notLive.innerHTML);
document.getElementById("div1").removeChild(document.getElementById("div2"));
console.log(notLive.innerHTML);
// 上面输出都是2,说明getElementById()也是**非**live collection

getElementsByTagName()

document.getElementsByTagName() 方法返回一个实时的包含具有给出标签名的元素们的HTMLCollection。指定的元素的子树会被搜索,包括元素自己。返回的 list 是实时的(live collection),意味着它会随着DOM树的变化自动更新。因此,如果对同一个元素,使用相同的参数,是不需要多次调用document.getElementsByTagName() 的。

Element.getElementsByTagName()的搜索被限制为指定元素的后代而不是document

1
2
3
4
5
var live = document.getElementsByTagName("p");
console.log(live[0].innerHTML);
document.getElementById("div1").removeChild(document.getElementById("div2"));
console.log(live[0].innerHTML);
// 第一个输出2,第二个报错,因为无法引用到p标签

getElementsByName()

该方法返回一个实时的nodelist collection,包含文档中所有name属性匹配的标签。这是一个live collection。
注意:在IE和opera下,如果某个元素1的name和另一个元素2的id重合,且元素2在元素1的前面,则getElementsByName()会取到元素2。

1
2
3
4
5
6
var live = document.getElementsByName("div2");
console.log(live[0].innerHTML);
document.getElementById("div1").removeChild(document.getElementById("div2"));
console.log(live[0].innerHTML);
// chrome下:全部输出4
// IE下: 第一个输出2,第二个报错。

getElementsByClassName()

该方法返回一个即时更新的(live) HTMLCollection,包含了所有拥有指定 class 的子元素。当在 document 对象上调用此方法时,会检索整个文档,包括根元素。(IE9以下不支持)

要匹配多个class,则className用空格分开。

1
getElementsByClassName("class1 class2");

1
2
3
4
5
var live = document.getElementsByClassName("one");
console.log(live[0].innerHTML);
document.getElementById("div1").removeChild(document.getElementById("div2"));
console.log(live[0].innerHTML);
// 第一个返回2,第二个报错

属性操作

setAttribute()

添加一个新属性(attribute)到元素上,或改变元素上已经存在的属性的值。

当在 HTML 文档中的 HTML 元素上调用 setAttribute() 方法时,该方法会将其属性名称(attribute name)参数小写化。

如果指定的属性已经存在,则其值变为传递的值。如果不存在,则创建指定的属性。也可指定为null。如果设置为null,最好使用removeAttribute()。

1
2
3
var div2 = document.getElementById("div2");
div2.setAttribute("class", "new_class");
div2.setAttribute("id", "new_id");

注意:在IE7下,修改了元素的class,如果已有class,则会出现两个class,通过setAttribute()添加的不生效;如果没有class,则添加上class,但这个添加上去的class的样式不会生效。

removeAttribute()

该方法用于移除元素的属性。

1
2
var div2 = document.getElementById("div2");
div2.removeAttribute("class");

注意:IE7下无法移除 class 属性

getAttribute()

该方法返回元素上指定属性(attribute)的值。如果指定的属性不存在,则返回 null 或 “” (空字符串)(IE5+都返回null)。

1
2
3
var div2 = document.getElementById("div2");
var attr = div2.getAttribute("class");
console.log(attr);

注意:IE7下不能正确返回class,返回的是null,其他正常。

hasAttribute()

hasAttribute() 返回一个布尔值,指示该元素是否包含有指定的属性(attribute)。

注意:IE7不支持该方法。

自定义属性data-*

html5里有一个data-*去设置获取元素的自定义属性值。

1
<div id="div1" data-aa="11">

利用div1.dataset可以获得一个DOMStringMap,包含了元素的所有data-*。
使用div1.dataset.aa就可以获取11的值。
同样,通过设置div1.dataset.bb = “22”就可以设置一个自定义属性值。
在不兼容的浏览器里,就使用getAttribute和setAttribute

1
2
3
4
5
6
7
8
9
10
var div1 = document.getElementById("div1");
var a = null;
if (div1.dataset) {
a = div1.dataset.aa;
div1.dataset.bb = "222";
} else {
a = div1.getAttribute("data-aa");
div1.setAttribute("data-bb", "2222");
}
console.log(a);

事件

addEventListener()

addEventListener()将指定的事件监听器注册到目标对象上,当目标对象触发指定的事件时,指定的回调函数就会触发。目标对象可以是 文档上的元素、 document、 window 或者XMLHttpRequest(比如onreadystatechange事件)。

IE8及以下不支持此方法且只有事件冒泡没有事件捕获。IE9开始支持此方法,也就有了事件捕获。

1
2
3
4
5
6
7
8
var div1 = document.getElementById("div1");
div1.addEventListener("click", listener, false);
function listener() {
console.log('test');
}
var cloneHtml = div1.cloneNode(true);
document.body.appendChild(cloneHtml);

第一个参数是事件名,第二个是回调函数,第三个参数为true表示捕获,false表示冒泡。

1
2
3
4
5
6
7
8
9
10
11
var div1 = document.getElementById("div1");
div1.addEventListener("click", listener1, true/fasle);
function listener1() {
console.log('test1');
}
var div2 = document.getElementById("div2");
div2.addEventListener("click", listener2, true/fasle);
function listener2() {
console.log('test2');
}

有一点要注意的是,当对某一个元素1既绑定了捕获事件,又绑定了冒泡事件时:
当这个元素1并不是触发事件的那个元素2时,则触发顺序会按照先 捕获 后 冒泡 的顺序触发;
当这个元素1就是最底层的触发事件的元素时,则这个元素没有捕获和冒泡的区别,谁先绑定就先触发谁。

1
2
3
4
5
6
7
8
9
10
11
12
13
var div2 = document.getElementById("div2");
div2.addEventListener("click", listener2, true);
function listener2() {
console.log('test2');
}
div2.addEventListener("click", listener1, false);
function listener1() {
console.log('test1');
}
// 按绑定顺序执行,两个`addEventLister()`颠倒过来则执行顺序也变化
// 如果再对`div1`绑定一个捕获、一个冒泡,则会先触发捕获 再 触发冒泡,与绑定顺序无关

removeEventListener()

与addEventListener()绑定事件对应的就是移除已绑定的事件。第三个参数的布尔值代表解绑的是捕获事件还是冒泡事件。两个事件互不相关。

1
2
3
4
5
6
var div2 = document.getElementById("div2");
div2.addEventListener("click", listener2, true);
function listener2() {
console.log('test2');
}
div2.removeEventListener("click", listener2, true);

注意:只能通过removeEventListener()解绑有名字的函数,对于绑定的匿名函数无法解除绑定。

1
2
3
4
5
6
7
8
9
10
div2.addEventListener("click", function(){
console.log('test');
console.log(this);
}, true);
div2.removeEventListener("click", function() {
console.log("test");
}, true);
div2.onclick = null;
// 点击div2依然打印出test

注意:这里this指向触发事件的元素自身。

attachEvent()、detachEvent()

IE8及以下使用这两个方法绑定和解绑事件,当然,IE9+也支持这个事件。但这个方法绑定的事件默认为冒泡也只有冒泡。

1
2
3
4
5
6
7
// 这里需要在事件前加 on
div2.attachEvent("onclick", listener1);
function listener1() {
console.log('test');
console.log(this);
}
div2.detachEvent("onclick", listener1);

和addEventListener()一样,也不能解绑匿名函数。
注意:这里this指向 window。

阻止默认事件和冒泡

标准事件和IE事件中的阻止默认事件和冒泡事件也有很大区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var div2 = document.getElementById("div2");
if (div2.addEventListener) {
div2.addEventListener("click", function(e) {
e.preventDefault(); // 阻止默认事件
e.stopPropagation(); // 阻止冒泡
console.log(e.target.innerHTML);
}, false);
} else {
div2.attachEvent("onclick", function() {
var e = window.event;
e.returnValue = false; // 阻止默认事件
e.cancelBubble = true; // 阻止冒泡
console.log(e.srcElement.innerHTML);
});
}

IE8及以下的event是绑定在window上的。(我的IE11里,仿真到IE7、IE8也可以取到标准事件里的 e 对象,估计是升级到IE11的原因)。

自定义事件:createEvent()

createEvent()用于创建一个新的 event ,而后这个 event 必须调用它的 init() 方法进行初始化。最后就可以在目标元素上使用dispatchEvent()调用新创建的event事件了。

createEvent()的参数一般有:UIEvents、MouseEvents、MutationEvents、HTMLEvents、Event(s)等等,分别有对应的init()方法。HTMLEvents、Event(s)对应的都是initEvent()方法。

1
initEvent(type, bubbles, cancelable)

type表示自定义的事件类型,bubbles表示是否冒泡,cancelable表示是否阻止默认事件。

1
target.dispatchEvent(ev)

target就是要触发自定义事件的DOM元素

1
2
3
4
5
6
7
8
9
10
11
12
13
var div1 = document.getElementById("div1");
div1.addEventListener("message", function(){
console.log('test');
}, false);
var div2 = document.getElementById("div2");
div2.addEventListener("message", function(e){
console.log(this);
console.log(e);
}, false);
var ev = document.createEvent("Event");
ev.initEvent("message", false, true); // 起泡参数变为true,div1的事件就会触发
div2.dispatchEvent(ev);

获取元素相关计算后的值

getComputedStyle()、currentStyle()

当我们想获取元素计算后实际呈现在页面上的各个值,就用这两个方法。IE8及以下用currentStyle(),IE9+及其他标准浏览器用getComputedStyle()。

1
2
3
4
5
6
7
8
9
var div2 = document.getElementById("div2");
var result = "";
if (window.getComputedStyle) {
result = (window || document.defaultView).getComputedStyle(div2, null)['cssFloat'];
} else {
result = div2.currentStyle["styleFloat"];
}
console.log(result);
// document.defaultView返回document对象所关联的window

这两个方法在不同的浏览器里差距也很大。
比如float属性:
getComputedStyle: IE9以上需要用cssFloat,其他标准的用float
currentStyle: IE8及以下可用styleFloat或者float。

比如height属性:
假如未设置height值,标准浏览器里能计算出高度值,而currentStyle计算出来是auto。

上面的例子getComputedStyle是用键值去访问的,也可用getPropertyValue()去访问。(IE8、IE7不支持)

1
result = (window || document.defaultView).getComputedStyle(div2, null).getPropertyValue("float");

getBoundingClientRect()、getClientRects()

getBoundingClientRect()该方法获得页面中某个元素的上、右、下、左分别相对浏览器视窗的位置。getBoundingClientRect是DOM元素到浏览器可视范围的距离(到浏览器顶部而不是文档顶部)。该函数返回一个Object对象,该对象有6个属性:top,lef,right,bottom,width,height;这里的top、left和css中的理解很相似,width、height是元素自身的宽高,但是right,bottom和css中的理解有点不一样。right是指元素右边界距窗口最左边的距离,bottom是指元素下边界距窗口最上面的距离。

getClientRects()是返回一个ClientRectList集合。

1
2
3
4
5
var div1 = document.getElementById("div1");
var rects1 = div1.getClientRects();
var rects2 = div1.getBoundingClientRect();
console.log(rects1[0].top);
console.log(rects2.top);

viewport和media query

发表于 2017-04-11

viewport

width=device-width:

你可以定义viewport的宽度.如果你不使用width=device-width,在移动端上你的页面延伸会超过视窗布局的宽度(width=980px),如果你使用了width=device-width,你的页面将会显示为合适的移动端宽度(width=320px),我们可以使用meta标记:

1
<meta name="viewport" content="width=device-width">

我们看见很多网站都建议把content属性的值设置为width=device-width。这相当于告诉浏览器将页面宽度假设为设备宽度。不幸的是,只有当设备是纵向时假设才是正确的。当我们把设备旋转成横向时,device-width还是和纵向的一样(比如,320px),这意味着,即使我们把页面设计成适应了480px横向设备,它还是会返回320px的效果。
如果我们的页面在纵向和横向设备中样式相同,那么我们就可以用width=device-width就足够了,需要注意的是这个是唯一告诉android设备使用设备宽度的方法。

initial-scale=1.0,maximum-scale=1.0

在大多数手机上,默认的缩放在手机浏览器上可能会触发”zoom”.为了阻止用户缩放,你可以设置initial-scale=1.0,下面是移动视窗的完整写法:

1
<meta name="viewport" content="width=device-width, initial-scale=1.0">

Media Queries

1
2
3
4
5
@media screen and (max-width: 600px) { /*当屏幕尺寸小于600px时,应用下面的CSS样式*/
.class {
background: #ccc;
}
}

要注意的是由于网页会根据屏幕宽度调整布局,所以不能使用绝对宽度的布局,也不能使用具有绝对宽度的元素。这一条非常重要,否则会出现横向滚动条。

CSS3的性能与兼容性

发表于 2017-04-11

性能

translate3d进行gpu加速

1
2
3
4
/**不推荐**/
left: 500px
/**推荐**/
transform: translateX(500px);

一个元素通过translate3d右移500px的动画流畅度会明显优于使用left属性;
原因是因为:

  • CSS动画属性会触发整个页面的重排relayout、重绘repaint、重组recomposite

  • Paint通常是其中最花费性能的,尽可能避免使用触发paint的CSS动画属性,这也是为什么我们推荐在CSS动画中使用webkit-transform: translateX(3em)的方案代替使用left: 3em,因为left会额外触发layout与paint,而webkit-transform只触发整个页面composite(这也是为什么推荐在CSS动画中使用webkit-transform: translateX(500px)的方案代替使用left: 500px);

box-shadow和gradients(渐变)

这两个东西在css3里往往都是页面的性能杀手,计算他们尤其消耗cpu, 尤其是在一个元素同时都使用了它们,所以拥抱扁平化设计吧;

兼容性

border-radius

IE9+,Firefox4+,Chrome5+,Safari5+,Opera01.5+,iOS Safari4+,Android Browser2.2+ ,Android Chrome18+

低版本的chrome:-webkit-border-radius:10px;

低版本的firefox:-moz-border-radius:10px;

IE6/7/8:引入ie-css3兼容文件,不支持除了黑色(#000)以外的其他颜色

box-shadow

IE9.0+,Firefox4.0+,Chrome10.0+,Safari5.1+,Opera10.5+,iOS Safari5.0+,Android Browser4.0+,Android Chrome18.0+

低版本的chrome:-webkit-box-shadow:10px 10px 5px #888;

低版本的firefox:-moz-box-shadow:10px 10px 5px #888;

IE6/7/8:

  • 使用filter
  • 推荐:引入ie-css3兼容文件behavior:url(ie-css3.htc)

border-image

IE9+,Firefox4+, Chrome15+,Safari7+, Opera15+, iOS Safari7+, Android Browser4.4+, Android Chrome18+

低版本的chrome:-webkit-background-size:10px 10px 5px #888; //不支持background简写

低版本的firefox:-moz-background-size:10px 10px 5px #888;

IE8 引入backgroundsize.min.htc兼容文件

text-shadow

IE10+, Firefox3.5+, Chrome4.0+, Safari6.0+

低版本的chrome:-webkit-text-shadow:1px 1px 1px #000;

低版本的firefox:-moz-text-shadow:1px 1px 1px #000;

IE6/7/8:引入ie-css3兼容文件behavior:url(ie-css3.htc);

transform

IE9+, Firefox3.5+, Chrome4.0+, Safari6.0+, iOS Safari8.4+, Android Browser4.4+, Android Chrome34+

兼容方法:

1
2
3
4
5
6
7
.transform{
-webkit-transform: x,y;
-moz-transform: x,y;
-ms-transform: x,y;
-o-transform: x,y;
transform: x,y;
}

IE8及以下:用IE滤镜

1
2
3
4
{
filter:fliph;//水平翻转相当于transform:rotateY(180deg)
filter:flipv;//垂直翻转相当于transform:rotateX(180deg)
}

transition

IE10+, Firefox16+, Chrome26+ ,Safari6.1+ , iOS Safari7+, Android Browser4.4+, Android Chrome25+

兼容方法:

1
2
3
4
5
6
p {
-webkit-transition: all .5s ease-in-out 1s;
-moz-transition: all .5s ease-in-out 1s;
-o-transition: all .5s ease-in-out 1s;
transition: all .5s ease-in-out 1s;
}

IE9以及更早的版本,不支持 transition 属性。

animation

IE10+,Firefox16+, Chrome43+, Safari9+
兼容方法:
低版本的chrome:-webkit-

低版本的firefox:-moz-

IE9及以下不支持

linear-gradient radial-gradient

IE10+, Firefox16+, Chrome26+, Safari6.1+
兼容方法:

低版本的chrome:-webkit-

低版本的firefox:-moz-

IE9及以下可使用 IE 滤镜处理:

1
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#000000',endColorstr='#ffffff');

rgba(r,g,b,a)

IE9+, Firefox2+, Chrome4+, Safari3+, iOS Safari3.2+, Android Browser2.1+, Android Chrome18+

兼容方法:

IE6/7/8不支持使用 rgba 模式实现透明度,可使用 IE 滤镜处理

1
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#7fff0000,endColorstr=#7fff0000);

flex

IE11+,Firefox22+, Chrome21+, Safari6.1+

低版本的chrome:-webkit- 或者 -webkit-box-flex

低版本的firefox:-moz-box-flex:1;

IE10:-ms-flex:1;

box-flex效果类似于过渡版本和新版本的flex属性;

CSS3新特性

发表于 2017-04-11

CSS3 选择器(Selector)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
body > .mainTabContainer tbody:nth-child(even){
background-color: white;
}
body > .mainTabContainer tr:nth-child(odd){
background-color: black;
}
:not(.textinput){
font-size: 12px;
}
div:first-child{
border-color: red;
}

如上所示,我们列举了一些 CSS3 的选择器,在我们日常的开发中可能会经常用到,这些新的 CSS3 特性解决了很多我们之前需要用 JavaScript 脚本才能解决的问题。
tbody: nth-child(even), nth-child(odd):此处他们分别代表了表格(tbody)下面的偶数行和奇数行(tr),这种样式非常适用于表格,让人能非常清楚的看到表格的行与行之间的差别,让用户易于浏览。
: not(.textinput):这里即表示所有 class 不是“textinput”的节点。
div:first-child:这里表示所有 div 节点下面的第一个直接子节点。
除此之外,还有很多新添加的选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
e:nth-last-child(n)
e:nth-of-type(n)
e:nth-last-of-type(n)
e:last-child
e:first-of-type
e:only-child
e:only-of-type
e:empty
e:checked
e:enabled
e:disabled
e:selection
e:not(s)

@Font-face 特性

1
2
3
4
5
6
7
8
9
10
@font-face {
font-family: BorderWeb;
src:url(BORDERW0.eot);
}
@font-face {
font-family: Runic;
src:url(RUNICMT0.eot);
}
.border { FONT-SIZE: 35px; COLOR: black; FONT-FAMILY: "BorderWeb" }
.event { FONT-SIZE: 110px; COLOR: black; FONT-FAMILY: "Runic" }

上边代码声明的两个服务端字体,其字体源指向“BORDERW0.eot”和“RUNICMT0.eot”文件,并分别冠以“BorderWeb”和“Runic”的字体名称。声明之后,我们就可以在页面中使用了:“ FONT-FAMILY: “BorderWeb” ” 和 “ FONT-FAMILY: “Runic” ”。
这种做法使得我们在开发中如果需要使用一些特殊字体,而又不确定客户端是否已安装时,便可以使用这种方式。

Word-wrap & Text-overflow 样式

“word-wrap: break-word”,设置或检索当当前行超过指定容器的边界时是否断开转行
“text-overflow” 则设置或检索当当前行超过指定容器的边界时如何显示(显示省略号)

边框和颜色(color, border)

颜色的透明度

1
2
color: rgba(255, 0, 0, 0.75);
background: rgba(0, 0, 255, 0.75);

这里的“rgba”属性中的“a”代表透明度,也就是这里的“0.75”.

圆角案例

1
2
3
div {
border-radius: 15px;
}

CSS3 的渐变效果(Gradient)

线性渐变

左上(0% 0%)到右上(0% 100%)即从左到右水平渐变:

1
background-image:-webkit-gradient(linear,0% 0%,100% 0%,from(#2A8BBE),to(\#FE280E));

这里 linear 表示线性渐变,从左到右,由蓝色(#2A8BBE)到红色(#FE280E)的渐变。
还有复杂一点的渐变,如:水平渐变,33% 处为绿色,66% 处为橙色:

1
background-image:-webkit-gradient(linear,0% 0%,100% 0%,from(#2A8BBE),color-stop(0.33,#AAD010),color-stop(0.33,#FF7F00),to(#FE280E));

径向渐变

接下来我们要介绍径向渐变(radial),这不是从一个点到一个点的渐变,而从一个圆到一个圆的渐变。不是放射渐变而是径向渐变。来看一个例子

1
backgroud:-webkit-gradient(radial,50 50,50,50 50,0,from(black),color-stop(0.5,red),to(blue));

CSS3 的阴影(Shadow)和反射(Reflect)效果

首先来说说阴影效果,阴影效果既可用于普通元素,也可用于文字,参考如下代码:

1
2
3
4
5
6
.class1{
text-shadow:5px 2px 6px rgba(64, 64, 64, 0.5);
}
.class2{
box-shadow:3px 3px 3px rgba(0, 64, 128, 0.3);
}

设置很简单,对于文字阴影:表示 X 轴方向阴影向右 5px,Y 轴方向阴影向下 2px, 而阴影模糊半径 6px,颜色为 rgba(64, 64, 64, 0.5)。其中偏移量可以为负值,负值则反方向。元素阴影也类似。
接下来我们再来谈谈反射,他看起来像水中的倒影,其设置也很简单,参考如下代码:

1
2
3
4
.classReflect{
-webkit-box-reflect: below 10px
-webkit-gradient(linear, left top, left bottom, from(transparent),to(rgba(255, 255, 255, 0.51)));
}

CSS3 的背景效果

CSS3 多出了几种关于背景(background)的属性,我们这里会简单介绍一下:
首先:“Background Clip”,该属确定背景画区,有以下几种可能的属性:

  • background-clip: border-box; 背景从 border 开始显示 ;
  • background-clip: padding-box; 背景从 padding 开始显示 ;
  • background-clip: content-box; 背景显 content 区域开始显示 ;
  • background-clip: no-clip; 默认属性,等同于 border-box;

通常情况,我们的背景都是覆盖整个元素的,现在 CSS3 让您可以设置是否一定要这样做。这里您可以设定背景颜色或图片的覆盖范围。
其次:“Background Origin”,用于确定背景的位置,它通常与 background-position 联合使用,您可以从 border、padding、content 来计算 background-position(就像 background-clip)。

  • background-origin: border-box; 从 border. 开始计算 background-position;
  • background-origin: padding-box; 从 padding. 开始计算 background-position;
  • background-origin: content-box; 从 content. 开始计算 background-position;

还有,“Background Size”,常用来调整背景图片的大小,注意别和 clip 弄混,这个主要用于设定图片本身。有以下可能的属性:

  • background-size: contain; 缩小图片以适合元素(维持像素长宽比)
  • background-size: cover; 扩展元素以填补元素(维持像素长宽比)
  • background-size: 100px 100px; 缩小图片至指定的大小 .
  • background-size: 50% 100%; 缩小图片至指定的大小,百分比是相对包 含元素的尺寸 .

最后,“Background Break”属性,CSS3 中,元素可以被分成几个独立的盒子(如使内联元素 span 跨越多行),background-break 属性用来控制背景怎样在这些不同的盒子中显示。

  • background-break: continuous; 默认值。忽略盒之间的距离(也就是像元 素没有分成多个盒子,依然是一个整体一 样)
  • background-break: bounding-box; 把盒之间的距离计算在内;
  • background-break: each-box; 为每个盒子单独重绘背景。

这种属性让您可以设定复杂元素的背景属性。
最为重要的一点,CSS3 中支持多背景图片,参考如下代码:

1
2
3
4
div {
background: url(src/zippy-plus.png) 10px center no-repeat,
url(src/gray_lines_bg.png) 10px center repeat-x;
}

此为同一元素两个背景的案例,其中一个重复显示,一个不重复。参考一下效果图:

CSS3 的盒子模型

参考文章:
弹性布局(Flex布局)

CSS3 的 Transitions, Transforms 和 Animation

Transitions

先说说 Transition,Transition 有下面些具体属性:
transition-property:用于指定过渡的性质,比如 transition-property:backgrond 就是指 backgound 参与这个过渡
transition-duration:用于指定这个过渡的持续时间
transition-delay:用于制定延迟过渡的时间
transition-timing-function:用于指定过渡类型,有 ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier
可能您觉得摸不着头脑,其实很简单,我们用一个例子说明,参看一下如下代码:

1
2
3
4
5
6
7
8
9
10
11
<div id="transDiv" class="transStart"> transition </div>
.transStart {
background-color: white;
-webkit-transition: background-color 0.3s linear;
-moz-transition: background-color 0.3s linear;
-o-transition: background-color 0.3s linear;
transition: background-color 0.3s linear;
}
.transEnd {
background-color: red;
}

这里他说明的是,这里 id 为“transDiv”的 div,当它的初始“background-color”属性变化时(被 JavaScript 修改),会呈现出一种变化效果,持续时间为 0.3 秒,效果为均匀变换(linear)。如:该 div 的 class 属性由“transStart”改为“transEnd”,其背景会由白(white)渐变到红(red)。

Transform

再来看看 Transform,其实就是指拉伸,压缩,旋转,偏移等等一些图形学里面的基本变换。见如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.skew {
-webkit-transform: skew(50deg);
}
.scale {
-webkit-transform: scale(2, 0.5);
}
.rotate {
-webkit-transform: rotate(30deg);
}
.translate {
-webkit-transform: translate(50px, 50px);
}
.all_in_one_transform {
-webkit-transform: skew(20deg) scale(1.1, 1.1) rotate(40deg) translate(10px, 15px);
}

“skew”是倾斜,“scale”是缩放,“rotate”是旋转,“translate”是平移。最后需要说明一点,transform 支持综合变换。可见其效果图如下:

Animation

最后,我们来说说 Animation 吧。它可以说开辟了 CSS 的新纪元,让 CSS 脱离了“静止”这一约定俗成的前提。以 webkit 为例,见如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@-webkit-keyframes anim1 {
0% {
Opacity: 0;
Font-size: 12px;
}
100% {
Opacity: 1;
Font-size: 24px;
}
}
.anim1Div {
-webkit-animation-name: anim1 ;/*与上边定义对应*/
-webkit-animation-duration: 1.5s;
-webkit-animation-iteration-count: 4;
-webkit-animation-direction: alternate;
-webkit-animation-timing-function: ease-in-out;
}

CSS选择器优先级及其性能优化

发表于 2017-04-11

首先我们需要清楚,浏览器是如何读取选择器,以识别样式,并将相应的样式附于对应的HTML元素,达到美化页面的效果。Chris Coyier曾在《Efficiently Rendering CSS》一文中说过“浏览器读取你的选择器,遵循的原则是从选择器的右边到左边读取。换句话说,浏览器读取选择器的顺序是由右到左进行”。
比如说:

1
2
3
div.nav < ul li a[title]{
/* */
}

上面的实例来说,浏览器首先会尝试在你的HTML标签中寻找“a[title]”元素,接着在匹配“li和ul”,最后在去匹配“div.nav”。这就是前成所主的“选择器从右到左的原则”。
选择器的最后一部分,也就是选择器的最右边(在这个例子中就是a[title]部分)部分被称为“关键选择器”,它将决定你的选择器的效率如何?是高还是低。
那么如何让关键选择器更有效,性能化更高呢?其实很简单,主要把握一点“越具体的关键选择器,其性能越高”
选择器有一个固有的效率,执行效率的顺序如下:

  1. id选择器(#myid)
  2. 类选择器(.myclassname)
  3. 标签选择器(div,h1,p)
  4. 相邻选择器(h1+p)
  5. 子选择器(ul < li)
  6. 后代选择器(li a)
  7. 通配符选择器(*)
  8. 属性选择器(a[rel=”external”])
  9. 伪类选择器(a:hover,li:nth-child)

综合上面的顺序,我们清楚的知道,id和类名用于关键选择器上效率是最高的,而CSS3的仿伪类和属性选择器,虽然使用方便,但其效率却是最低的。
这个顺序跟选择器的优先级是有区别的:

  1. important声明 1,0,0,0
  2. ID选择器 0,1,0,0
  3. 类选择器 0,0,1,0
  4. 伪类选择器 0,0,1,0
  5. 属性选择器 0,0,1,0
  6. 标签选择器 0,0,0,1
  7. 伪元素选择器 0,0,0,1
  8. 通配符选择器 0,0,0,0

几种清除浮动的方法及优缺点

发表于 2017-04-11

1. 父级div定义height

1
2
3
4
5
6
7
8
9
10
11
12
13
<style type="text/css">
.div1{background:#000080;border:1px solid red;/*解决代码*/height:200px;}
.div2{background:#800080;border:1px solid red;height:100px;margin-top:10px}
.left{float:left;width:20%;height:200px;background:#DDD}
.right{float:right;width:30%;height:80px;background:#DDD}
</style>
<div class="div1">
<div class="left">Left</div>
<div class="right">Right</div>
</div>
<div class="div2">
div2
</div>

原理:父级div手动定义height,就解决了父级div无法自动获取到高度的问题。
优点:简单、代码少、容易掌握
缺点:只适合高度固定的布局,要给出精确的高度,如果高度和父级div不一样时,会产生问题
建议:不推荐使用,只建议高度固定的布局时使用

2. 结尾处加空div标签 clear:both

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<style type="text/css">
.div1{background:#000080;border:1px solid red}
.div2{background:#800080;border:1px solid red;height:100px;margin-top:10px}
.left{float:left;width:20%;height:200px;background:#DDD}
.right{float:right;width:30%;height:80px;background:#DDD}
/*清除浮动代码*/
.clearfloat{clear:both}
</style>
<div class="div1">
<div class="left">Left</div>
<div class="right">Right</div>
<div class="clearfloat"></div>
</div>
<div class="div2">
div2
</div>

原理:添加一个空div,利用css提高的clear:both清除浮动,让父级div能自动获取到高度
优点:简单、代码少、浏览器支持好、不容易出现怪问题
缺点:增加空div,让人感觉很不好
建议:不推荐使用,但此方法是以前主要使用的一种清除浮动方法

3. 父级div定义伪类:after 和 zoom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style type="text/css">
.div1{background:#000080;border:1px solid red;}
.div2{background:#800080;border:1px solid red;height:100px;margin-top:10px}
.left{float:left;width:20%;height:200px;background:#DDD}
.right{float:right;width:30%;height:80px;background:#DDD}
/*清除浮动代码*/
.clearfloat:after{
display:block;
clear:both;content:"";visibility:hidden;height:
}
.clearfloat{
zoom:1
}
</style>
<div class="div1 clearfloat">
<div class="left">Left</div>
<div class="right">Right</div>
</div>
<div class="div2">
div2
</div>

原理:IE8以上和非IE浏览器才支持:after,原理和方法2有点类似,zoom(IE转有属性)可解决ie6,ie7浮动问题
优点:浏览器支持好、不容易出现怪问题(目前:大型网站都有使用,如:腾迅,网易,新浪等等)
缺点:代码多,要两句代码结合使用才能让主流浏览器都支持。

4. 父级div定义 overflow:hidden

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<style type="text/css">
.div1{
background:#000080;border:1px solid red;
/*解决代码*/
width:98%;
overflow:hidden
}
.div2{background:#800080;border:1px solid red;height:100px;margin-top:10px;width:98%}
.left{float:left;width:20%;height:200px;background:#DDD}
.right{float:right;width:30%;height:80px;background:#DDD}
</style>
<div class="div1">
<div class="left">Left</div>
<div class="right">Right</div>
</div>
<div class="div2">
div2
</div>

原理:必须定义width或zoom:1,同时不能定义height,使用overflow:hidden时,浏览器会自动检查浮动区域的高度
优点:简单、代码少、浏览器支持好
缺点:不能和position配合使用,因为超出的尺寸的会被隐藏。
建议:只推荐没有使用position或对overflow:hidden理解比较深的朋友使用。

弹性布局(Flex布局)

发表于 2017-04-11

1. Flex布局是什么?

Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。
任何一个容器都可以指定Flex布局

1
2
3
.box{
display: flex;
}

行内元素也可以使用Flex布局。

1
2
3
.box{
display: inline-flex;
}

Webkit内核的浏览器,必须加上-webkit前缀。

1
2
3
4
.box{
display: -webkit-flex; /* Safari */
display: flex;
}

注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。

2. 基本概念

采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

3. 容器的属性

以下6个属性设置在容器上。

  • flex-direction
  • flex-flow
  • justify-content
  • align-items
  • align-content

3.1 flex-direction属性

flex-direction属性决定主轴的方向(即项目的排列方向)

1
2
3
.box {
flex-direction: row | row-reverse | column | column-reverse;
}

它可能有4个值。

  • row(默认值):主轴为水平方向,起点在左端。
  • row-reverse:主轴为水平方向,起点在右端。
  • column:主轴为垂直方向,起点在上沿。
  • column-reverse:主轴为垂直方向,起点在下沿。

3.2 flex-wrap属性

默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

1
2
3
.box{
flex-wrap: nowrap | wrap | wrap-reverse;
}

它可能取三个值。

  • nowrap(默认):不换行。
  • wrap:换行,第一行在上方。
  • wrap-reverse:换行,第一行在下方。

3.3 flex-flow

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

1
2
3
.box {
flex-flow: <flex-direction> || <flex-wrap>;
}

3.4 justify-content属性

justify-content属性定义了项目在主轴上的对齐方式。

1
2
3
.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
}

它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。

  • flex-start(默认值):左对齐
  • flex-end:右对齐
  • center: 居中
  • space-between:两端对齐,项目之间的间隔都相等。
  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

3.5 align-items属性

align-items属性定义项目在交叉轴上如何对齐。

1
2
3
.box {
align-items: flex-start | flex-end | center | baseline | stretch;
}

它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。

  • flex-start:交叉轴的起点对齐。
  • flex-end:交叉轴的终点对齐。
  • center:交叉轴的中点对齐。
  • baseline: 项目的第一行文字的基线对齐。
  • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

3.6 align-content属性

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

1
2
3
.box {
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

该属性可能取6个值。

  • flex-start:与交叉轴的起点对齐。
  • flex-end:与交叉轴的终点对齐。
  • center:与交叉轴的中点对齐。
  • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
  • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
  • stretch(默认值):轴线占满整个交叉轴。

3. 项目的属性

以下6个属性设置在项目上。

  • order
  • flex-grow
  • flex-shrink
  • flex-basis
  • flex
  • align-self

4.1 order属性

order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

1
2
3
.item {
order: <integer>;
}

4.2 flex-grow属性

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

1
2
3
.item {
flex-grow: <number>; /* default 0 */
}

如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。

4.3 flex-shrink属性

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

1
2
3
.item {
flex-shrink: <number>; /* default 1 */
}

如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
负值对该属性无效。

4.4 flex-basis属性

flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

1
2
3
.item {
flex-basis: <length> | auto; /* default auto */
}

它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。

4.5 flex属性

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

1
2
3
.item {
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}

该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。

4.6 align-self属性

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

1
2
3
.item {
align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

该属性可能取6个值,除了auto,其他都与align-items属性完全一致。

CSS实现水平、垂直居中的8中方法

发表于 2017-04-07

脱离文档流的居中

margin:auto法

我们经常用margin:0 auto来实现水平居中,而一直认为margin:auto不能实现垂直居中……实际上,实现垂直居中仅需要声明元素高度和下面的CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
div{
width: 400px;
height: 400px;
position: relative;
border: 1px solid #465468;
}
img{
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}

负margin法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.container{
width: 500px;
height: 400px;
border: 2px solid #379;
position: relative;
}
.inner{
width: 480px;
height: 380px;
background-color: #746;
position: absolute;
top: 50%;
left: 50%;
margin-top: -190px; /*height的一半*/
margin-left: -240px; /*width的一半*/
}

未脱离文档流元素的居中

table-cell法

1
2
3
4
5
6
7
8
9
10
11
div{
width: 300px;
height: 300px;
border: 3px solid #555;
display: table-cell;
vertical-align: middle;
text-align: center;
}
img{
vertical-align: middle;
}
123
sunzhen

sunzhen


github主页

23 日志
10 标签
© 2017 sunzhen
由 Hexo 强力驱动
主题 - NexT.Pisces