0.介绍

0.1介绍

浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)
MDN-Web API

0.2组成结构

  • ECMAScript-JavaScript的核心
    定义了javaScrip的语法规范
    javaScrip的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准与具体实现无关

  • BOM浏览器对象模型
    一套操作浏览器功能的API
    通过BOM可以操作浏览器窗口,比如:弹出窗,控制浏览器跳转,获取分辨率等

  • DOM文档对象模型
    一套操作页面元素的API
    DOM可以把HTML看作是文档树,通过DOM提供的API可以对树上的节点进行操作

DOM

1.1 DOM概念

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM。
DOM是一种基于树的API文档,他在处理过程中整个文档都加载到内存中。
DOM文档树模型如下
如图所示

  • 文档:一个网页可以成为文档
  • 节点:网页中的所有内容都是节点(标签,属性,文本,注释等)
  • 元素:网页中的标签
  • 属性:标签的属性

DOM经常进行的操作

  • 获取元素
  • 对元素进行操作(设置其属性或调用其防范)
  • 动态创建元素
  • 事件(给元素绑定事件)

1.2 document获取标签

HTML是从上向下加载,如果把js写在head中,在HTML还没有加载完全就去获取dom元素会找不到

console.dir(box) 输出对象

1
2
3
4
5
var box = document.getElementById('box');
// 输出的是元素对应的标签内容
console.log(box)
// 打印对象
console.dir(box)

获取对象类型两个方法

  • typeof
    typeof不能获取对象的具体类型,获取对象类型始终返回object
  • construction
    对象.construction可以获取到具体对象

  • 根据id获取元素

    1
    2
    3
    var div = document.getElementById('main');
    console.log(div)
    // 获取到的数据类型HTMLDivElement,对象都是有类型的
  • 根据标签名称获取元素
    返回的是节点集合

    1
    2
    3
    4
    5
    var divs = document.getElementsByTagName('div');
    for(var i = 0;i < divs.length;i++){
    var div = divs[i];
    console.log(div)
    }
  • 根据name获取元素
    返回的是节点集合

    1
    2
    3
    4
    5
    var divs = document.getElementsByName('sex');
    for(var i = 0;i < divs.length;i++){
    var div = divs[i];
    console.log(div)
    }
  • 根据class获取元素
    返回的是节点集合
    这个是IE9以后才支持的(class是关键字,所以采用classname)

    1
    2
    3
    4
    5
    var divs = document.getElementsByClassName('sex');
    for(var i = 0;i < divs.length;i++){
    var div = divs[i];
    console.log(div)
    }
  • HTML5中获取元素
    在HTML5中新增的元素获取方法querySelector

查找一个元素,如果多个只返回第一个
querySelector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// class="lover"
var div = document.querySelector('.lover')
// 获取p元素
var node = document.querySelector("p");
// 通过id属性取得元素
var oBox = document.querySelector('#box');
//通过属性选择器取得元素
var oTest = body.querySelector('[title="test"]');
// 获取class=”lover” 的第一个p元素
var node = document.querySelector("p.lover");
// 获取第一个带target属性的a元素
var node = document.querySelector("a[target]");
// 获取第一个输入框
var node = document.querySelector("input[type=text]");

查找多个元素
querySelectorAll

1
2
3
4
5
6
7
8
//取得所有div元素
var div = document.querySelectorAll("div")
//取得所有class为"in"的元素
var oIn = document.querySelectorAll('.in');
//取得title属性为test的元素
var oTest = body.querySelectorAll('[title="test"]');
//取得body元素
var body = document.querySelectorAll("body")[0]

总而言之,这个方法相对于上面方法就是取多个

1.3 绑定事件

事件的三要素:

  • 事件源(谁能触发事件)
  • 事件名称(怎么能触发,点击还是经过等)
  • 事件处理函数
1
2
3
4
5
6
7
8
9
// 获取超链接
var link = documentById('link');
// 注册事件
link.onclick = function(){
alert('hello')
// 取消后续内容的执行
return false;
}
}

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1.获取元素
var btn = document.getElementById('btn');
var box = document.getElementById('box');
// isShow记录box的显示和隐藏 true显示false隐藏
var isShow = true;
// 2.给按钮注册事件
btn.onclick = function(){
// 当前box是显示的状态
if(isShow){
// 3 控制div的显示隐藏(这里需要写一个class样式为隐藏)
box.className = 'hidden'
// 修改按钮上的文字
btn.value = '显示';
isShow = false;
}else{
box.className = 'show'
btn.value = '隐藏';
isShow = true;
}
}

1.4 this

  • 函数中的this
    window对象

    1
    2
    3
    function fn(){
    console.log(this);
    }
  • 方法中的this
    调用该方法的对象

    1
    2
    3
    4
    5
    6
    7
    var obj = {
    name:'zs',
    say:function(){
    console.log(this);
    }
    }
    obj.say();
  • 构造函数中的this
    当前对象

  • 事件处理函数中的this
    触发事件的对象,事件源

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1.获取所有a标签对应的元素
var imagegallert = documentById('imagegallery');
var links = imagegallert.getElementsByTagName('a');
// 2.给所有的a元素注册点击事件
var i=0,len = links.length;
for(; i<len; i++){
// 获取当前元素
var link = link [i];
link.onclick = function(){
// 3.切换图片和文字
var image = document.getElementById("image");
var des = document.getElementById("des");
// 设置图片这里触发事件的时候(被点击时)i已经是4了,所以需要用this
// image.src = link.href;
image.src = this.href;
// 设置文字这里触发事件的时候(被点击时)i已经是4了,所以需要用this
// des.innerText = link.title;
des.innerText = this.title;
// 取消默认行行为
return false;
}
}

1.5 innerHTML和innerText

  1. 获取标签之间的内容

    • innerHTML获取内容的时候,会把标签也获取到
    • innerText获取内容的时候会把标签过滤掉
  2. 设置标签之间的内容

    • innerHTML设置内容的时候,会把特殊字符进行标签化处理
    • innerText不会进行标签化处理
  3. 案例

    1
    2
    3
    box.innerHTML = "linjia<b>d</b><p>这是一个段落</p>"
    box.innerHTML = "linjia&lt;b&gt;d&lt;/b&gt;&lt;p&gt;这是一个段落&lt;/p&gt;"
    box.innerText = "linjia<b>d</b><p>这是一个段落</p>"
    • < <
    • < >
    • &nbsp 空格
    • © @

innerHTML 不是标准的DOM属性
innerText 最早IE中增加的属性,老版本的firefox不支持此属性,老版本的firefox用的是textContent

  1. 案例
    写一个函数,处理innerText和textContent的兼容性问题
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 获取元素之间的内容
    function getInnerText(element) {
    // 判断element是否支持innerText
    if(typeof element.innerHTML === "string"){
    return element.innerHTML;
    }else{
    return element.textContent;
    }
    }
    // 设置元素之间的内容
    function setInnerText(element,content) {
    // 判断element是否支持innerText
    if(typeof element.innerHTML === "string"){
    element.innerHTML = content;
    }else{
    element.textContent = content;
    }
    }

1.6 表单元素属性

  1. 设置文本框中的内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // 1. 设置文本框中的内容
    var inputs = document.getElementsByTagName("input");
    var i = 0,len = inputs.length;
    for(;i <len;i++){
    // 获取集合中的元素
    var input = inputs[i];
    // 判断当前的input是否是文本框
    if(input.type === "text"){
    input.value = i + 1;
    }
    }

    // 2. 点击按钮获取文本框中的内容,并用|分割形成一个新的字符串
    var btn = document.getElementById("btn");
    btn.onclick = function() {
    // 找到所有的文本框,获取文本框的值
    var str = '';
    // 定义数组,避免拼接字符串耗费性能
    var arr = [];
    for (i = 0;i<len;i++){
    var input = inputs[i];
    // 判断是否是文本框
    if(input.type !== "text"){
    continue;
    }
    // str += input.value + "|";
    arr.push(input.value);
    }
    // str = str.substr(0,str.length - 1);
    str = arr.join("|");
    console.log(str);
    }
  2. 切换图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 记录索引 默认是0
var index = 0 ;
// 1 给按钮注册事件
var btn = document.getElementById("btn");
btn.onclick = function() {
var img = document.getElementById("img");
// 2 切换到下一张
index ++;
img.src = arr[index];
// 3 如果是最后一张切换到第一张
if(index === arr.length - 1){
index = -1;
}
}
  1. 检测用户名密码
    检验用户名是3-6位,密码是6-8位,如果不满足条件要求高亮显示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 1 给登录按钮注册点击事件
    var btnLogin = document.getElementById("btnLogin");
    btnLogin.onclick = function() {
    // 检测用户名是否是3-6位,不满足要求高亮显示文本框
    // 文本框对应元素
    var texName = document.getElementById("name");
    if(texName.value.length < 3 || texName.value.length > 6){
    // 文本框高亮显示(这里需要先定义css)
    texName.className = 'bg';
    // 阻止表单提交
    return false;
    }else{
    texName.className = '';
    }
    // 3 密码是否是6-8位 不满足高亮显示
    var texPwd = document.getElementById("pwd");
    if(texPwd.value.length < 6 || texPwd.value.length > 8){
    // 文本框高亮显示(这里需要先定义css)
    texPwd.className = 'bg';
    // 阻止表单提交
    return false;
    }else{
    texPwd.className = '';
    }
    }
  2. 输入框默认值
    当文本框获得焦点的时候onfocus,如果文本框中的内容是《请输入搜索关键字》则清空文本框,设置黑色字体

    onfocus 获得焦点事件

onblur 失去焦点事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 提取公共方法
function my$(id) {
return document.getElementById(id);
}

var tXtSearch = my$("tetSearch");
// 当文本框获得焦点的时候
tXtSearch.onfocus = function() {
// 判断当前文本框中的文字
if(this.value === "请输入搜索关键字"){
this.value = '';
this.className = '';
}
}

// 当失去焦点时候,如果文本框中的内容为空则设置文本框内容为《请输入搜索关键字》
tXtSearch.onblur = function() {
// 判断当前文本框中的文字
if(this.value.length === 0){
this.value = '请输入搜索关键字';
this.className = 'gray';
}
}
  1. checkbox全选,反选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 1. 点击全员按钮(父的checkbox)的时候,让子的checkbox的选中状态跟其保持一致
var j_cbAll = document.getElementById("j_cbAll");
// 1.2 让所有的子checkbox的选中状态和父checkbox保持一致
var checkboxs = document.querySelectorAll("#j_tb input[type=checkbox]");
var i = 0,len = checkboxs.length;
j_cbAll.onclick = function() {
for (;i < len;i++){
// 获取每一个checkbox
var checkbox = checkboxs[i];
// 让每一个checkbox的选中状态和父checkbox保持一致
checkbox.checked = this.checked;
}
}

// 2. 给所有的子的checkbox注册点击事件,点击子的checkbox,如果有一个没被选中,则父checkbox也不被选中
// 2.1 给所有的子checkbox注册点击事件
for(i=0;i < len;i++){
// 获取每一个checkbox
var checkbox = checkboxs[i];
// 注册点击事件
checkbox.onclick = function() {
// 2.2 如果有一个子的checkbox没有被选中,父的checkbox也不被选中
//假设所有子checkbox都被选中了
var isAllChecked = true;
for(i = 0;i < len;i++){
// 获取每一个子的checkbox
checkbox = checkboxs[i];
// 判断选中状态
if(!checkbox.checked){
// 如果checkbox没有被选中,设置isAllChecked false
isAllChecked = false;
break;
}
}
// 设置父checkbox的选中状态
j_cbAll.checked = isAllChecked;
}
}
// 3. 反选
// 3.1 给反选按钮注册点击事件
document.getElementById("btn").onclick = function() {
// 3.2遍历所有子的checkbox,对其进行反选
for(i = 0;i < len;i++){
// 获取每一个子的checkbox
checkbox = checkboxs[i];
checkbox.checked = !checkbox.checked;
}
}

写到这里会有问题,当反选时无法控制父checkbox
所以我们把父checkbox的方法单独拿出来,修改代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 1. 点击全员按钮(父的checkbox)的时候,让子的checkbox的选中状态跟其保持一致
var j_cbAll = document.getElementById("j_cbAll");
// 1.2 让所有的子checkbox的选中状态和父checkbox保持一致
var checkboxs = document.querySelectorAll("#j_tb input[type=checkbox]");
var i = 0,len = checkboxs.length;
j_cbAll.onclick = function() {
for (;i < len;i++){
// 获取每一个checkbox
var checkbox = checkboxs[i];
// 让每一个checkbox的选中状态和父checkbox保持一致
checkbox.checked = this.checked;
}
}

// 2. 给所有的子的checkbox注册点击事件,点击子的checkbox,如果有一个没被选中,则父checkbox也不被选中
// 2.1 给所有的子checkbox注册点击事件
for(i=0;i < len;i++){
// 获取每一个checkbox
var checkbox = checkboxs[i];
// 注册点击事件
checkbox.onclick = function() {
// 调用公共方法控制父checkbox
singleChecked();
}
}
// 3. 反选
// 3.1 给反选按钮注册点击事件
document.getElementById("btn").onclick = function() {
// 3.2遍历所有子的checkbox,对其进行反选
for(i = 0;i < len;i++){
// 获取每一个子的checkbox
checkbox = checkboxs[i];
checkbox.checked = !checkbox.checked;
}
// 调用公共方法控制父checkbox
singleChecked();
}
// 单独提炼出控制父节点方法
function singleChecked() {
// 2.2 如果有一个子的checkbox没有被选中,父的checkbox也不被选中
//假设所有子checkbox都被选中了
var isAllChecked = true;
for(i = 0;i < len;i++){
// 获取每一个子的checkbox
checkbox = checkboxs[i];
// 判断选中状态
if(!checkbox.checked){
// 如果checkbox没有被选中,设置isAllChecked false
isAllChecked = false;
break;
}
}
// 设置父checkbox的选中状态
j_cbAll.checked = isAllChecked;
}

1.7 自定义属性

  • getAttribute() 获取标签内属性
  • setAttribute() 设置标签内属性
  • removeAttribute() 删除标签内属性
  • 与element.属性的区别:上诉三个方法用于获取任意的行内元素

案例

1
<div id="box" stuId="1">张三</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var box = document.getElementById("box");
// 这个是无法获取stuId的值(undefined)【在IE中可以,但是在chrome中不可以】
//console.log(box.stuId);

// var o ={};
// console.log(o.a);// 当访问对象不存在的属性,返回undefined

// 获取标签对应的自定义属性
console.log(box.getAttribute("stuId"));
// 也可以获取标签本身具有的属性
console.log(box.getAttribute("id"));

// 设置标签对应的自定义属性
box.setAttribute("test","hello");
// 也可以设置标签本身具有的属性
box.setAttribute("class","bg");

// 删除属性
box.removeAttribute("test");
box.removeAttribute("class");
// 不可以设置标签的自定义属性(在浏览器标签中看不到),但是box对象本身具有了abc属性
box.abc = "hello";
console.log(box.abc);

1.8 操作样式

设置style仅仅是操作style的样式并不能改变class写好的样式

  • 设置样式类(需要提前写class)
  • 设置style
  • 设置style.cssText
1
2
3
4
5
6
7
8
9
10
11
12
13
var box = document.getElementById("box");
// 1 设置类样式
box.className = "box";
// 2 设置style
// console.dir(box);可以看到里面有style属性,并且style属性中是一个json,其中每个属性都是string类型
box.style.width = "200px";
box.style.height = "200px";
box.style.backgroundColor = "yellow";

// cssText 获取标签的style属性中的字符串
// console.log(box.style.cssText)

box.style.cssText = "width:200px;height:200px";
  1. 案例:开关灯

document.body.style

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. 给按钮注册事件
var btn = document.getElementById("btn");
// 记录开关灯
var isOpen = true;
btn.onclick = function() {
if(isOpen){
// 如果当前是开灯状态,关灯
// 2 修改body的背景颜色
document.body.style.backgroundColor = "black";
this.value = "开灯";
isOpen = false;
}else{
document.body.style.backgroundColor = "";
this.value = "关灯";
isOpen = true;
}
}
  1. 案例:鼠标经过显示二维码图片

鼠标经过的事件 onmouseover 鼠标离开事件 onmouseout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取nodeSmall注册事件 鼠标经过的事件 onmouseover 鼠标离开事件 onmouseout
var node_small = document.getElementById("node_small");

// 鼠标经过显示二维码
node_small.onmousemove = function() {
// 把class为hide替换成show
document.getElementById("er").className = document.getElementById("er").className.replace('hide','show')
}

// 鼠标离开隐藏二维码
node_small.onmouseout = function() {
// 把class为show替换成hide
document.getElementById("er").className = document.getElementById("er").className.replace('show','hide')
}
  1. 案例:移动方块
1
2
3
4
5
6
7
8
9
10
11
12
13
document.getElementById("btn").onclick = function() {
var box = document.getElementById("box");

box.style.width = "200px";
box.style.height = "200px";

// 改成绝对定位
box.style.position = "absolute";
box.style.left = "200px";
box.style.top = "200px";

box.style.backgroundColor = "yellow";
}
  1. 案例:输入框输入时高亮

.onfocus获得焦点 .onblur 失去焦点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 给所有的文本框注册事件  获取焦点的事件
// 当文本框获得焦点之后 高亮显示 失去焦点 恢复
var texts = document.querySelectorAll("input[type=text]");

var i = 0,len text.length;
for(;i < len;i++){
var text = texts[i];
text.onfocus = function() {
// 高亮显示
this.style.backgroundColor = 'lightgray';
}

// 失去焦点
text.onblur=function() {
this.style.backgroundColor = '';
}
}
  1. 案例:各行变色,鼠标放上变高亮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 隔行变色 奇数red 偶数 green
// 获取所有的li
var list =document.querySelectorAll("#mv li");
var i = 0,len = lists.length;
// 存储改变之前的背景
var bg;
for(;i < len;i++){
// 判断i是奇数还是偶数
if(i%2 === 0){
// i是从0开始的,奇数行颜色
list[i].style.backgroundColor = 'red';
}else{
list[i].style.backgroundColor = 'green';
}

// 鼠标放上显示高亮
list[i].onmouseover = function() {
// 鼠标放到li上高亮显示
bg = this.style.backgroundColor;
this.style.backgroundColor = 'yellow';
}

// 鼠标离开不高亮
list[i].onmouseout = function() {
// 鼠标离开li还原原来的颜色
this.style.backgroundColor = bg;
}
}
  1. 案例:切换TAB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 1. 鼠标放到tab栏上高亮显示,其他tab取消高亮显示
// 1.1 获取hd中所有的span(标题栏中)。注册鼠标经过时间
var spans = document.querySelectorAll("#hd span")
var i = 0,len = spans.length;

for(;i < len;i++){
var span = spans[i];
// 注册事件
span.onmouseover = function() {
// 取消所有span的高亮显示
for(i = 0;i < len; i++){
spans[i].className = '';
}
// 1.2 高亮显示这个
this.className = 'current';

// 2 当tab栏高亮显示的时候。找到对应的详细内容显示,其他详细内容隐藏
// 2.1 给span记录索引
// 2.2 挡鼠标经过span的时候,获取span的索引
var index = parseInt(this.getAttribute("index"));
// 2.3 根据索引找详细内容的div显示其他的隐藏
var divs = document.querySelectorAll("#bd div");

// 让所有的详细内容的div隐藏
var divLen = divs.length;
for(i = 0;i<divLen;i++){
var div = divs[i];
div.className = "";
}

// 当前模块对应的div显示
divs[index].className = "current";
}
}

1.9 节点操作

想要构建如图所示的结构
如图所示

  • 元素节点
  • nodeName 标签的名称
  • nodeType 节点的类型 1 元素节点
  • nodeValue 当是元素节点的时候nodeValue始终是null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 元素节点
var box = document.getElementById("box");
console.dir(box);
// 元素节点
// nodeName 标签的名称
// nodeType 节点的类型 1 元素节点
// nodeValue 当是元素节点的时候nodeValue始终是null
//////////////////////////////////
var attr = box.getAttributeNode("id");
console.dir(attr);
// 属性节点
// 获取属性节点
// nodeName 标签的名称
// nodeType 节点的类型 2 属性节点
// nodeValue 属性的值
  1. 模拟DOM文档的结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 模拟DOM文档的结构
// 属性 id className nodeName nodeType nodeValue
// 构建构造函数
function Node(options) {
// 当不传参的时候 options是undefined
// 这样options.id会报错
// 给默认值
options = options || {};
this.id = options.id || "";
this.className = options.className || "";
this.nodeName = options.nodeName || "";
this.nodeType = options.nodeType || 1;
this.nodeValue = options.nodeValue || null;

// 设置一个属性记录子元素
this.children = [];
}
// 1. html元素
var html = new Node({
nodeName:"HTML"
});
// 2. head元素
var head = new Node({
nodeName:"HEAD"
});
html.children.push(head);
// 3. body元素
var body = new Node({
nodeName:"BODY"
});
html.children.push(body);
// 4. div元素
var div = new Node({
id: "box",
nodeName:"DIV"
});
body.children.push(div);
// 5. p元素
var p = new Node({
id:"p",
nodeName:"P"
});
body.children.push(p);

console.dir(html);
  1. 获取子节点
  • childNodes 所有子节点
  • children 子元素
  • parentNode 父元素
1
2
3
4
5
6
7
8
9
10
11
12
13
var box = document.getElementById("box");
// 获取box中所有的子节点
for(var i = 0;i < box.childNodes.length;i++){
// 便利所有的子节点,找到里面的元素nodeType === 1的是子元素
if(box.childNodes[i].nodeType === 1){
console.log(box.childNodes[i]);
}
}

// 获取子元素
console.log(box.children);
// 获取父元素
console.log(box.parentNode);
  1. 获取第一个/最后一个节点
  • .firstChild 第一个子节点
  • .firstElementChild 第一个子元素
  • .lastChild 最后一个子节点
  • .lastElementChild 最后一个子元素
1
2
3
4
5
6
7
8
9
10
var ul = document.getElementById("ul");
// 获取第一个子节点
console.dir(ul.firstChild);
// 获取第一个子元素
console.dir(ul.firstElementChild);

// 获取最后一个子节点
console.dir(ul.lastChild);
// 获取最后一个子元素
console.dir(ul.lastElementChild);

由于firstElementChild存在兼容性问题,我们可以自己封装一个方法来解决兼容性

1
2
3
4
5
6
7
8
9
10
11
12
13
function getFirstElementChild(parent) {
// 如果当前浏览器支持firstElementChild
if(parent.firstElementChild){
return parent.firstElementChild;
}
// 否则用下面的办法
var node,nodes = parent.childNodes,i = 0;
while (node = nodes[i++]){
if(node.nodeType === 1){
return node;
}
}
}
  1. 获取兄弟节点
  • .nextSibling 下一个兄弟节点
  • .nextElementSibling 下一个兄弟元素
  • .previousSibling 上一个兄弟节点
  • .previousElementSibling 上一个兄弟元素
1
2
3
4
5
6
7
8
9
// 获取下一个兄弟节点
console.log(li.nextSibling)
// 获取下一个兄弟元素
console.log(li.nextElementSibling)

// 获取上一个兄弟节点
console.log(li.previousSibling)
// 获取上一个兄弟元素(存在兼容性问题)
console.log(li.previousElementSibling)

2.0 a标签href

href=”javascript:void(0)”

这里有两部分首先是javascript表示我下面要说的都是用js来解析
还有其他如下

  • javascript: 用js来解析
    • <a href="javascript:void(0)">首页</a>
  • http:下面是连接,连接跳转
    • <a href="http://www.baidu.com">首页</a>
  • mailto: 下面是邮箱
    • <a href="mailto://xxx@xx.com">首页</a>
  • tel: 下面是手机号码拨打电话(在手机上使用)
    • <a href="tel://00000>首页</a>

void 关键字 void会执行()里面的语句表达式,总是返回undefined
就是什么都不做

1
2
var r = void(1+2);
console.log(r);

案例:高亮显示列表,并屏蔽a标签的点击事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 获取menu中所有的a
var menu = document.getElementById("menu");
// 获取第一个ul的方法有以下集中
// menu.children[0]
// menu.getElementsByTagName("ul")[0];
// menu.firstElementChild
// 获取ul
var ul = menu.firstElementChild;
var list = ul.children;
// 遍历li
var i = 0 ;len = list.length;
for(;i < len;i++){
// 获取li中的a
var link = list[i].firstElementChild;
// 注册事件 linkClick后面不带小括号(表示吧方法传递过来,待括号表示把返回值传递过来)
link.onclick = linkClick;
}
1
2
3
4
5
6
7
8
9
10
11
// 事件处理函数
function linkClick() {
// 取消所有li的高亮显示
for(i = 0;i<len;i++){
list[i].className = "";
}
// 当前a标签对应的li高亮显示
this.parentNode.className = "current";

return false;
}

2.1 document.write()

把整个页面都重写,只剩下write中的内容

  • 性能问题
    • innerHTML方法由于会对字符串进行解析,需要避免在循环内多次使用。
    • 可以借助字符串或数组的方式进行替换,再设置给innerHTML
    • 优化后与document.createElement性能相近
1
2
3
4
5
6
// 点击按钮
var btn = document.getElementById("btn");
btn.onclick = function() {
// 如果在事件中使用document.write会把整个页面覆盖
document.write("新设置的内容<p>标签也可以生成</p>")
}

innerText具有兼容性问题,这里我们下一个函数,处理兼容性问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 获取元素之间的内容
function getInnerText(element) {
// 判断element是否支持innerText
if(typeof element.innerText === "string"){
return element.innerText;
}else{
return element.textContent;
}
}

// 设置元素之间的内容
function setInnerText(element,content) {
// 判断element是否支持innerText
if(typeof element.innerText === "string"){
element.innerText = content;
}else{
element.textContent = content;
}
}

对比性能

  1. 第一种方法(循环innerHTML)

    console.time('flag')
    console.timeEnd('flag')
    查询开始到结束的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.time('flag')
var box = document.getElementById("box");
var data = ['zs','ls','ww'];
box.innerHTML = '<ul>'
// 遍历数据生成li
data.forEach(function(item,index) {
// forEach遍历数组,参数是一个函数
// 这个函数有两个参数,第一个参数是数组中的元素,第二个参数是索引
box.innerHTML +='<li>'+item+'</li>';


})
box.innerHTML +='</ul>'
// 输出执行time和timeEnd之间代码所消耗的时间
console.timeEnd('flag')
  1. 第二种方法(利用数组innerHTML)
1
2
3
4
5
6
7
8
9
10
11
12
13
console.time('flag')
var box = document.getElementById("box");
var data = ['zs','ls','ww'];
var arr = [];
arr.push('<ul>')
// 遍历数据生成li
data.forEach(function(item,index) {
arr.push('<li>'+item+'</li>');
})
arr.push('</ul>')
box.innerHTML = arr.join("");

console.timeEnd('flag')
  1. 第三种方法(利用document.createElement)

.removeChild() 移除元素
var r = confirm(“是否确定删除?”);
.appendChild() 添加子节点
document.createElement 创建节点

下面我们实现一个表格Table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 数据行中的数据
var bodyData = [
{name:'zs',subject:'语文',score:100},
{name:'ls',subject:'数学',score:80},
{name:'sdb',subject:'英语',score:0},
{name:'gw',subject:'体育',score:19},
{name:'bzr',subject:'数学',score:50},
]
// 表头数据

var headData = ["姓名""科目","成绩","操作"];

// 1.表头
var table = document.createElement("table");
document.getElementById("box").appendChild(table);
// 设置table样式
table.border = "1px";
table.width = "500px";
table.cellSpacing = 0;

var tead = document.createElement("tead");
table.appendChild(tead);

var tr = document.createElement("tr");
tead.appendChild(tr);
// 设置tr样式
tr.style.height = "50px";
tr.style.backgroundColor = "lightgray"

// 生成表头中的列
headData.forEach(function(item) {
var th = document.createElement('th');
tr.appendChild(th);
th.innerText = item;
})

// 2. 数据行
var tbody = document.createElement("tbody");
table.appendChild(tbody);
// 设置数据行的内容居中
tbody.style.textAlign = "center";

// 遍历数据
bodyData.forEach(function(item) {
// 创建行
// 便利对象
for(var key in item){
var td = document.createElement("td");
tr.appendChild(td);
td.innerText = item[key];
}
// 操作的列
td = document.createElement("td");
tr.appendChild(td);
// 创建删除超链接
var link = document.createElement("a");
link.href = "javascript:void(0)";
td.appendChild(link);
link.innerText = "删除";
// 给link注册删除事件
link.onclick = linkClick;
})

// 事件处理函数
function linkClick() {
// 提示
var r = confirm("是否确定删除?");
if(r){
// 删除
// 获取点击按钮所在的行
var tr = this.parentNode.parentNode;

tbody.removeChild(tr);
}
}

  1. 测试3种方式
1
2
3
4
5
6
7
8
9
10
var  d1 = +new Date();
var arr = [];

var str = "";

for(var i = 0;i < 1000; i ++){
document.body.innerHTML += '<div style="width:100px;height:2px:border:1px solid blue;"></div>';
}
var d2 = +new Date();
console.log(d2 - d1);
1
2
3
4
5
6
7
8
9
10
11
var  d1 = +new Date();
var arr = [];

var str = "";

for(var i = 0;i < 1000; i ++){
arr.push('<div style="width:100px;height:2px:border:1px solid blue;"></div>');
}
document.body.innerHTML = array.join("");
var d2 = +new Date();
console.log(d2 - d1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var  d1 = +new Date();
var arr = [];

var str = "";

for(var i = 0;i < 1000; i ++){
var div = document.createElement("div");
div.style.width="100px";
div.style.height = "2px";
div.style.border = "1px solid red"
document.body.appendChild(div);
}
document.body.innerHTML = array.join("");
var d2 = +new Date();
console.log(d2 - d1);

2.2 操作节点

insertBefore(div1,div2) 在div2前插入div1
removeChild(div) 删除div节点
replaceChild(div1,div2) 把div2替换成div1

在ul中第三个位置之前插入li

1
2
3
4
5
6
var ul = document.getElementById("ul");

var li = document.createElement("li");

li.innerText = "测试";
ul.insertBefore(li,ul.children[2])

替换元素

1
2
3
4
var ul = document.getElementById("ul");
var div = document.createElement("div");
div.innerText = "div";
ul.replaceChild(div,ul.children[0]);

删除ul中所有li

如果使用innerHtml="",不会删除元素身上的事件对应的函数,容易造成内存泄漏

1
2
3
4
5
var ul = document.getElementById("ul");
for(var i = 0.len = ul.children.length;i<len;i++){
var li = ul.children[i];
ul.removeChild(li);
}

案例:权限选择

<select id="select" multiple="multiple">

这里还有第一点需要说明,循环document.get出list的时候,如果删除了其中一个。document.get的length会改变,里面的元素会重新创建索引,这时候在删除第二个(刚才删除第一个)实际上删除的是刚才的第三个

如果appendChild是其他地方的元素,在appendChild的同时原来的元素就没有了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 存放所有权限的列表
var all = document.getElementById("all");
// 存放选中的权限列表
var select = document.getElementById("select");

// 1.全选
document.getElementById("bt1").onclick = function() {
var i = 0,len = all.children.length;
for(;i < len ; i++){
var li = all.children[0];
select.appendChild(li);
}
}
// 2. 全不选

// 3. 选择选中的选项

document.getElementById("bt3").onclick = function() {
for(i = 0;i<len;i++){
var li = all.children[i];
// 判断li是否选中
if(li.selected){
select.appendChild(li)
i --;
}
}
}

// 4.移除选中的选项

2.3 注册事件

注册事件的三种方式

  1. onclick
    如果有一个元素两次onclick,第二次会覆盖第一次。
1
2
3
4
5
6
var box = document.getElementById("box");
box.onclick = function() {
console.log("点击后执行")
}
// 取消点击
box.onclick = null;
  1. addEventListenner(W3C标准)

box.addEventListener(“click”,eventCode,false)

  • 第一个参数 事件名称(不带on)
  • 第二个参数 事件处理函数
  • 第三个参数 事件冒泡(false)/事件捕获(true)
1
2
3
4
5
6
7
8
9
var box = document.getElementById("box");
box.addEventListener("click",eventCode,false);
// 取消点击
box.removeEventListener("click",eventCode,false);

function eventCode() {
console.log("点击后执行")
console.log(this); // 这里的this是触发事件的对象
}
  1. attachEvent(IE9以前)
1
2
3
4
5
6
7
var box = document.getElementById("box");
box.attachEvent("onclick".enentCode);
// 取消点击
box.detachEvent("onclick".enentCode);
function eventCode() {
console.log("点击后执行")
}

注册和移除事件处理兼容性问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 注册事件
function addEventListener(element,eventName,callback){
if(element.addEventListener){
element.addEventListener(eventName,callback,false);
}else if(element.attachEvent){
element.attachEvent("on" + eventName,callback);
}else{
element["on" + eventName] = callback;
}
}
// 移除事件
function removeEventListener(element,eventName,callback) {
if(element.removeEventListener){
element.removeEventListener(eventName,callback,false);
}else if(element.attachEvent){
element.detachEvent("on" + eventName,callback);
}else{
element["on" + eventName] = null;
}
}

2.4 事件流(冒泡/ 捕获)

DOM标准采用捕获+冒泡。两种事件流都会触发DOM的所有对象,从document对象开始,也在document对象结束。
如图所示
事件的三个阶段/事件流

  1. 事件捕获阶段
  2. 目标阶段
  3. 冒泡阶段
  • 冒泡型事件流:事件的传播是从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。【推荐】

  • 捕获型事件流:事件的传播是从最不特定的事件目标到最特定的事件目标。即从DOM树的根到叶子

阻止事件冒泡 e.stopPropagation();

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv">Click me!</div>
</body>
</html>

在冒泡型事件流中click事件传播顺序为<div>—》<body>—》<html>—》document
在捕获型事件流中click事件传播顺序为document—》<html>—》<body>—》<div>
如图所示如图所示

2.5 事件委托

把本来应该li做的事情,转交给父元素来做(事件冒泡)

1
2
3
4
5
6
7
var ul = document.getElementById("ul");
ul.onclick = function(e) {
// 当事件发生的时候,系统会把事件发生时候的一些数据传递给事件处理函数
// e事件对象(事件参数)
// e.target 真正触发事件的对象 li
e.target.style.backgroundColor = "red";
}

2.6 事件对象属性和方法

  • event.type 获取事件类型
  • clientX/clientY 所有浏览器都支持,窗口位置
  • pageX/pageY IE8以前不支持,页面未知
  • event.target||event.srcElement用于获取触发事件的元素

获取是什么事件触发的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var  box = document.getElementById("box");
box.onclick = fn;
box.onmousemove = fn;
box.onmouseout = fn;

function fn(e) {
// e有兼容性问题,IE9以前不支持
// ie老版本获取事件对象的方式window.event

// 处理兼容性问题
e = e || window.event;
switch (e.type){
case "click":
console.log("点击事件");
break;
case "mouseover":
console.log("鼠标经过");
break;
case "mouseout":
console.log("鼠标离开");
break;
}
}

案例:根据鼠标位置改变图片位置

这里有个坑,document.onmousemovedocument.body.onmousemove的区别
body有效范围是bodyn内部元素的范围,这里就是图片那么大的范围

clientX和clientY互殴的是鼠标在可视区域中的位置
pagX和pagY获取的是在页面中的位置

1
2
3
4
5
6
7
8
9
10
11
12
var img = document.getElementById("img");
document.onmousemove = function(e) {
e = e || window.event;

img.style.position = "absolute";
// img.style.left = e.clientX + "px";
// img.style.top = e.clientY + "px";

// 获取鼠标在页面中的位置
img.style.left = e.pageX + "px";
img.style.top = e.pageY + "px";
}

如果上下左右有滚动条时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

var img = document.getElementById("img");
document.onmousemove = function(e) {
e = e || window.event;
img.style.position = "absolute";
img.style.left = getPage(e).pageX + "px";
img.style.top = getPage(e).pageY + "px";
}
// 获取鼠标在页面上的坐标
function getPage(e){
return{
pageX:e.clientX + getScroll().scrollLeft,
pageY:e.clientY + getScroll().scrollTop
}
}

// 获取页面滚出去的距离(处理兼容性)
function getScroll() {
return{
scrollTop:document.documentElement.scrollTop || document.body.scrollTop,
scrollLeft:document.documentElement.scrollLeft || document.body.scrollLeft,
}
}

2.7 阻止事件传播方式

  • 标准方式:event.stopPropagation();
  • IE低版本:event.cancelBubble = true; 标准中已废弃
1
2
3
4
5
6
7
var link = document.getElementById("link");
link.onclick = function(e) {
alert("点击了");
// 阻止默认行为
// return false;
e.preventDefault();
}
  • event.stopPropagation()用法介绍

    • 该方法将停止事件的传播,阻止它被分派到其他 Document 节点。在事件传播的任何阶段都可以调用它。
  • event.preventDefault()用法介绍

    • 将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。

2.7 常用的鼠标键盘事件

  • onmouseup 鼠标按键放开时触发
  • onmousedown 鼠标按键按下触发
  • onmousemove 鼠标移动触发
  • onkeyup 键盘按下触发(这个阻止方法执行,否则down来不及)
  • onkeydown 键盘按键抬起触发
1
2
3
4
5
6
7
8
9
var txt = document.getElementById("txt");
txt.onkeydown = function(e) {
// 键盘码
console.log(e.keyCode);

if(e.keyCode === 65){
return false;
}
}

想让用户按下空格input光标换到下一个input

use strict

第一种思路把keyCode改成9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'use strict'

a = 5;// 严格模式下必须先声明变量再使用
function f() {
console.log(this);// 严格模式下this是undefined
}
var texts = document.querySelectorAll("input[type = text]";)

var i=0,len = texts.length;
for(;i<len;i++){
var txt = texts[i];
txt.onkeydown = txtKeydown;
}

function txtKeydown() {
// 13回车 9 tab
if(e.keyCode === 13){
e.keyCode = 9;
}
}

如果加了严格模式会报错,因为keyCode是只读属性

当在文本框中按下回车,让下一个文本框获取焦点

nextElement.focus();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'use strict'

var texts = document.querySelectorAll("input[type = text]";)

var i=0,len = texts.length;
for(;i<len;i++){
var txt = texts[i];
txt.onkeydown = txtKeydown;
}

function txtKeydown() {
// 当在文本框中按下回车,让下一个文本框获取焦点
var nextElement = this.nextElementSibling;
if(nextElement){
nextElement.focus();
}
}

最后更新: 2019年01月14日 21:06

原始链接: http://linjiad.github.io/2019/01/07/WebApi/

× 请我吃糖~
打赏二维码