元素(element)和节点(node)

childNode属性和children属性的区别

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 <div id="parentDiv">
     <!--注释-->
     <div></div>
 </div>
 <script>
     console.log(document.getElementById('parentDiv').childNodes); //NodeList[5]
     console.log(document.getElementById('parentDiv').children) ;  //HTMLCollection[1]
 </script>
</body>
</html>

JS高级语法与JS选择器-风君雪科技博客

childNodes属性返回的是NodeList数组,是属于节点(也包括元素)的属性,而children属性返回的是子元素,是属于元素的属性

而在Nodelist数组里面也有元素存在,证明了元素也是节点的一种,即元素节点。

W3C中的解释是:

在 HTML DOM (文档对象模型)中,每个部分都是节点

文档本身是文档节点
所有 HTML 元素是元素节点
所有 HTML 属性是属性节点
HTML 元素内的文本是文本节点 (包括回车符,空格,空白字符也是属于文本节点)
注释是注释节点

Element 对象可以拥有类型为元素节点、文本节点、注释节点的子节点。

NodeList 对象表示节点列表,比如 HTML 元素的子节点集合。

元素也可以拥有属性。属性是属性节点。
总结:元素是元素节点,是节点中的一种,但元素节点中可以包含很多的节点

nodeName(纯大写) 属性含有某个节点的名称

元素节点的nodeName 是标签名称
属性节点的nodeName 是属性名称
文本节点的nodeName 永远是 #text
文档节点的nodeName 永远是 #document

nodeValue 对于文本节点,nodeValue 属性包含文本。 对于属性节点,nodeValue 属性包含属性值
nodeValue 属性对于文档节点和元素节点是不可用的 

nodeType 属性可返回节点的类型

Element 元素     1
Attribute 属性   2
Text 文本        3
CDATA Section CDATA断     4
Entity Reference 实体参数  5
Entity 实体       6
Processing Instrucion 处理指令  7
Comment 注释    8
Document 文档    9
Document Type 文档类型   10
Document Fragment 文档片断   11

添加和删除节点(HTML 元素)

创建新的 HTML 元素

如需向 HTML DOM 添加新元素,您必须首先创建该元素(元素节点),然后向一个已存在的元素追加该元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="div1">
    <p id="p1">这是一个段落</p>
    <p id="p2">这是另一个段落</p>
</div>

</body>
<script>
var para=document.createElement("p");

var node=document.createTextNode("这是新段落。");
para.appendChild(node);

var element=document.getElementById("div1");
element.appendChild(para);
</script>
</html>

<!-- 为这段代码创建新的 <p> 元素:
var para=document.createElement("p");
此时检查页面可以看到div里面已经生成了<p>元素

如需向 <p> 元素添加文本,您必须首先创建文本节点。
这段代码创建了一个文本节点:
var node=document.createTextNode("这是新段落。");
然后您必须向 <p> 元素追加这个文本节点:
para.appendChild(node);
最后您必须向一个已有的元素追加这个新元素。
这段代码找到一个已有的元素:
var element=document.getElementById("div1");
这段代码向这个已有的元素追加新元素:
element.appendChild(para); -->

JS高级语法与JS选择器-风君雪科技博客

删除已有的 HTML 元素

如需删除 HTML 元素,您必须首先获得该元素的父元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="div1">
<p id="p1">这是一个段落。</p>
<p id="p2">这是另一个段落。</p>
</div>
 
</body>
<script>
var parent=document.getElementById("div1");

var child=document.getElementById("p1");

parent.removeChild(child);
</script>
</html>

<!-- 例子解释:
这个 HTML 文档含有拥有两个子节点(两个 <p> 元素)的 <div> 元素:
<div id="div1">
<p id="p1">这是一个段落。</p>
<p id="p2">这是另一个段落。</p>
</div>

找到 id="div1" 的元素:
var parent=document.getElementById("div1");
找到 id="p1" 的 <p> 元素:
var child=document.getElementById("p1");

从父元素中删除子元素:
parent.removeChild(child);

提示:如果能够在不引用父元素的情况下删除某个元素,就太好了。
不过很遗憾。DOM 需要清楚您需要删除的元素,以及它的父元素。
这是常用的解决方案:
找到您希望删除的子元素,然后使用其 parentNode 属性来找到父元素:
var child=document.getElementById("p1");
child.parentNode.removeChild(child); -->

常用节点操作

1.节点查找
document.getElementById,document.getElementByTagName,document.getElementByName ,document.getElementByClassName
document.querySelector() 参数为选择器
document.forms 选取页面中的所有表单元素
2.增加节点
增加节点前必须先使用document.createElement()创建元素节点,参数为标签名
m.appendChild(n) 为m元素在末尾添加n节点
m.insertBefore(k,n) 在m元素的k节点前添加n节点
3.删除节点
m.removeChild(n)删除m元素中的n节点
m.replaceChild(k,n)用n节点取代m元素中的k节点
4.复制节点
m.cloneChild() 复制m节点,并将复制出来的节点作为返回值
参数为true时,则将m元素的后代元素也一并复制。否则,仅复制m元素本身


节点属性操作

节点(自身)属性:
  
attributes - 节点(元素)的 属性节点
nodeType – 节点类型
nodeValue – 节点值
nodeName – 节点名称
innerHTML - 节点(元素)的文本值

1.节点属性值选取
m.属性名  (:驼峰形式 )     m.className
m[“属性名”]      m.['className']

m.getAttribute(“属性名”)  :加引号,html的形式       m.getAttribute("class")

2.节点属性修改
前两种选取方法时,直接赋值即可
m.setAttribute("属性名",“值”)


3.创建属性节点并设置属性值
var info_node=document.createAttribute("info");//创建
info_node.value='123';//设置
sup1.setAttributeNode(info_node);//添加

4.复制节点
var body = document.querySelector('body');
true深拷贝,拷贝自身与内容, false浅拷贝,只拷贝自身标签
var cl_body = body.cloneNode(true);
console.log(cl_body);


 
JS DOM节点(当前标签和同级、父级、子级..之间的关系)
1. 通过顶层document节点获取
    1) document.getElementById(elementId) //根据id获得
    2) document.getElementsByName(elementName) //根据name获得
    3) document.getElementsByTagName(tagName) //根据标签名获得
2、通过父节点获取
    1) parentObj.firstChild //获得第一个子节点
    2) parentObj.lastChild //获得第二个子节点
    3) parentObj.childNodes //获取作为指定对象直接后代的HTML元素和TextNode对象的集合
    4) parentObj.children //非标准dom集合,建议使用childNodes
    5) parentObj.getElementsByTagName(tagName) //获得该标签下标签名为tagName的所有标签
3、通过临近节点获取
    1) neighbourNode.previousSibling //获得同级前一个标签
    2) neighbourNode.nextSibling //获得同级后一个标签
4、通过子节点获取
    1) childNode.parentNode //获得父标签

事件

onload:页面加载完毕事件,只附属于window对象
onclick:鼠标点击时间
onmouseover:鼠标悬浮事件
onmouseout:鼠标移开事件

on事件绑定方式

document.onclick = function() {
console.log("文档点击");
}

on事件只能绑定一个方法,重复绑定保留最后一次绑定的方法

document.onclick = function() {
console.log("文档点击");
}

事件的移除

document.onclick = null;

非on事件绑定方式

document.addEventListener('click', function() {
console.log("点击1");
})
document.addEventListener('click', function() { console.log("点击2"); })

非on事件可以同时绑定多个方法,被绑定的方法依次被执行
addEventListener第三个参数(true|false)决定冒泡的方式

function fn () {}
document.addEventListener('click', fn);

事件的移除
document.removeEventListener(‘click’, fn,false);

事件的三种绑定方式的思考

方法一:嵌入dom
把onclick绑定在标签上
HTML元素行间事件(也可以叫HTMl事件处理程序),直接在html标签里添加事件。
缺点:html和js代码紧密耦合
<input id="btn1" type="button" onclick="test();" />
 
方法二:直接绑定
onclick的js绑定办法

//把一个函数赋值给一个事件处理程序属性。(这种方式也叫做Dom0级事件处理程序)
var btn1 = document.getElementById('btn1');
function abc() {
    alert('abc');
}
btn1.onclick = abc; //当点击的时候执行abc这个函数,等价于 btn1.onclick=function abc(){alert('abc');}
//btn1.onclick = null; //去掉绑定的事件

方法三:事件监听

 click的js绑定办法(js的话推荐这种)
//通过“事件监听”的方式来绑定事件(也叫Dom2级事件处理程序)

var btn2 = document.getElementById('btn2');
btn2.addEventListener('click', showMsg, false); //鼠标单击的时候调用showMes这个函数  
function showMsg() {
    alert("事件监听");
}
//btn2.removeEventListener('click', showMsg, false); //去除绑定


方法一
最原始的写法:和html混合在一起写,缺点是代码高冗余,且无法添加多个事件处理函数如上文对事件的举例则为典型的**html事件处理程序*写法

方法二
dom0级:将html代码和js代码分离,且支持匿名函数,可以看到完美的改进了1的冗余缺憾,所有的事件相关操作都在js中完成

方法三
dom2级:ie使用attachEventListener其他非ie使用addEventListener,可以支持绑定多个事件,瞧吧,又一个缺憾被完美解决了~,而且dom2级还可以自定义事件流,下一篇会分析事件流模型

dom3级:对事件进行了更广而全的分类,请自行查阅

实例运用一

</head>
<body>
    <div class="div"></div>
    <div class="div"></div>
</body>
<script>
    // js事件: 页面标签在满足某种条件下可以完成指定功能的这种过程, 成之为事件
    // 某种条件: 如鼠标点击标签: 点击事件 | 鼠标双击标签: 双击事件 | 键盘按下: 键盘按下事件
    // 指定功能: 就是开发者根据实际需求完整相应的功能实现

    // 钩子函数: 就是满足某种条件被系统回调的函数(完成指定功能)

    // 点击事件: 明确激活钩子的条件 = 激活钩子后改处理什么逻辑, 完成指定功能(函数)
    var div = document.querySelector(".div");  // 找到的是第一个.div
    div.onclick = function () {
        // alert(123)
        this.style.backgroundColor = "pink";
    }

    // 明确第一个及第二个
    var divs = document.querySelectorAll('.div');
    divs[1].ondblclick = function () {
        divs[0].style.backgroundColor = "yellow";
    }

</script>
</html>

实例运用二

复习并延伸
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>复习预习</title>
    <style>
        [key='value'] {
            color: #0f8209;
        }
    </style>
</head>
<body>
    <div class="ele" alert="OK">div div div</div>
</body>
<script>
    // 1.面向对象js
    // ES5
    // 普通的单一对象
    var obj = {
        // 普通对象的属性
        key: "value",
        fn: function () {
            console.log("普通对象的方法")
        }
    };
    console.log(obj.key);
    console.log(obj["key"]);
    // 1.key的类型为字符串类型
    // 结论:
    //      js支持的标识符可以省略引号, 反之不可以省略
    //      不支持的标识符访问方式: 不可以采用.语法,需要采用[]语法,eg:obj["background-color"]

    var obj1 = {
        "name": "obj1",
        // key有时候会出现js不能直接支持的标识符书写方式
        // 需求: obj1用来描述页面标签的各种颜色
        color: "red",
        // "color": "red",
        "background-color": "yellow"
    }
    console.log(obj1.name);
    console.log(obj1["name"]);
    console.log(obj1.color);
    // obj1.background = 12;
    // color = 10;
    console.log(obj1["background-color"]);

    // 2. 对象可以任意添加或删除属性
    var obj2 = {
        name: "obj2"
    };
    console.log(obj2);
    // 删除属性
    delete obj2.name;
    console.log(obj2);
    // 添加属性
    obj2.age = 8;
    console.log(obj2);

    // 拓展: 获取的页面元素就是标签对象, 可以对其添加任意属性
    var ele = document.querySelector('.ele');
    console.log(ele.info);  // 直接使用无值, 原因ele并没有添加该属性
    ele.info = "添加的属性信息";  // 添加属性
    console.log(ele.info);  // 添加属性后就可以正常方式添加的属性值
    delete ele.info;  // 删除操作
    console.log(ele.info);  // 删除后属性又会消失

    // 构造函数
    function Perple(name, age) {
        this.name = name;
        this.age = age;
        this.fn = function () {
            console.log("fn")
        }
    }
    // 实例化对象
    var p = new Perple("张三", 18);
    p.fn();

    // ES6
    class Student {
        constructor (name, age) {
            this.name = name;
            this.age = age;
        }
        fn () {
            console.log("fn")
        }
    }
    var s = new Student("张三", 18);
    s.fn();

</script>
<script>
    // getElementById只能由document调用
    var ele = document.getElementsByClassName("ele")[0];
    console.log(ele);
    ele = document.querySelector(".ele");
    console.log(ele);
    ele = document.querySelectorAll(".ele")[0];
    console.log(ele);

    // 该添加属性的方式只映射到js代码中
    ele.index = 123;
    console.log(ele.index);

    // js如何操作元素(页面标签)的全局属性, 映射到html代码中
    ele = document.querySelector('[alert]');  // 通过全局属性获取元素
    console.log(ele);
    // 获取全局属性值
    var info = ele.getAttribute('alert');
    console.log(info);
    // 修改全局属性值
    ele.setAttribute('alert', 'no ok');
    // 添加全局属性值(映射到html代码中) => 结合CSS来控制页面标签的样式
    ele.setAttribute('key', 'value');

</script>
</html>

事件参数event

存放事件信息的回调参数

在触发DOM上某个事件时,会产生一个事件对象event,这个对象包含着所有事件有关的信息(导致事件的元素、事件的类型、与特定事件相关的信息)
所有浏览器都支持Event对象,但支持方式不同
IE中的事件对象:window.event

阻止事件冒泡 

DOM中提供stopPropagation()方法,但IE不支持,使用event对象在事件函数中调用就行
IE中提供的是,cancelBubble属性,默认为false,当它设置为true时,就是阻止事件冒泡,也是用event对象在事件函数中调用 
 
jQuery中提供了stopPropagation()方法来停止事件冒泡,当需要时,只需用用event对象来调用就行,即event.stopPropagation();

默认行为

阻止默认行为 
DOM中提供preventDefault()方法来取消事件默认行为,但是只有当cancelable属性设置为true的事件,才可以使用preventDefault()来取消事件默认行为,使用event对象在事件函数中调用就行
IE中提供的是returnValue属性,默认为true,当它设置为false时,就是取消事件默认行为,也是用event对象在事件函数中调用
jQuery中提供了preventDefault()方法来阻止元素的默认行为,只需要用event对象来调用就好,即event.preventDefault()
如果想同时对事件对象停止冒泡和默认行为,可以在事件处理函数中返回false。这是对事件对象同时调用stopPropagation()方法和preventDefault()方法的一种简写方式 

事件注意点

1、event代表事件的状态,例如触发event对象的元素、鼠标的位置及状态、按下的键等等;
2、event对象只在事件发生的过程中才有效。
firefox里的event跟IE里的不同,IE里的是全局变量,随时可用;
firefox里的要用参数引导才能用,是运行时的临时变量

在IE/Opera中是window.event,在Firefox中是event;
而事件的对象,在IE中是window.event.srcElement,在Firefox中是event.target,Opera中两者都可用

处理冒泡与默认事件

事件的冒泡:父子都具有点击事件,不处理的话,点击子级也会出发父级的点击事件

如果提供了事件对象,则这是一个非IE浏览器
if ( e && e.stopPropagation )
因此它支持W3C的stopPropagation()方法
e.stopPropagation(); 
else
否则,我们需要使用IE的方式来取消事件冒泡 
window.event.cancelBubble = true;
return false;

处理冒泡实例

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>事件对象</title>
    <style>
        body {
            margin: 0;
        }
        .box {
            background-color: pink;
        }
        .sup {
             200px;
            height: 200px;
            background-color: red;
        }
        .sub {
             100px;
            height: 100px;
            background-color: orange;
        }
    </style>
</head>
<body>
    <div class="box">12345</div>

    <div class="sup">
        <div class="sub"></div>
    </div>

    <a href="https://www.baidu.com">只想相应点击事件</a>
</body>
<script>
    var box = document.querySelector('.box');
    // 事件的钩子函数, 系统回调时传递了一个值, 该值为事件对象
    box.onclick = function (ev) {  // 回调函数
        console.log(ev)
        // 特殊按键 altKey | shiftKey | ctrlKey
        console.log(ev.altKey)
        // 鼠标的点击点
        console.log(ev.clientX, ev.clientY)
    }
</script>
<script>
    var sup = document.querySelector('.sup');
    var sub = document.querySelector('.sub');

    // 事件默认有冒泡, 子级相应事件后,会将事件传递给父级,如果父级有相同事件,也会被激活, 最终传递给document
    sub.onclick = function (ev) {
        console.log(ev);
        // 取消冒泡, 当自身处理事件后, 该事件就处理完毕, 结束, 不再向上传递
        ev.cancelBubble = true;
        console.log("子级被点击了")
    };
    sup.onclick = function () {
        console.log("父级被点击了")
    };
    document.onclick = function () {
        console.log("文档被点击了")
    }
</script>
</html>

事件默认行为:当一个事件发生时浏览器自己会做的事情

如果提供了事件对象,则这是一个非IE浏览器 
if ( e && e.preventDefault ) 
阻止默认浏览器动作(W3C) 
e.preventDefault(); 
else
IE中阻止函数器默认动作的方式 
window.event.returnValue = false; 
return false;

既然return false 和 e.preventDefault()都是一样的效果,那它们有区别吗?当然有
仅仅是在HTML事件属性 和 DOM0级事件处理方法中 才能通过返回 return false 的形式组织事件宿主的默认行为

处理默认事件实例

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>事件对象</title>
    <style>
        body {
            margin: 0;
        }
        .box {
            background-color: pink;
        }
        .sup {
             200px;
            height: 200px;
            background-color: red;
        }
        .sub {
             100px;
            height: 100px;
            background-color: orange;
        }
    </style>
</head>
<body>
    <div class="box">12345</div>

    <div class="sup">
        <div class="sub"></div>
    </div>

    <a href="https://www.baidu.com">只想相应点击事件</a>
</body>
<script>
    var box = document.querySelector('.box');
    // 事件的钩子函数, 系统回调时传递了一个值, 该值为事件对象
    box.onclick = function (ev) {  // 回调函数
        console.log(ev)
        // 特殊按键 altKey | shiftKey | ctrlKey
        console.log(ev.altKey)
        // 鼠标的点击点
        console.log(ev.clientX, ev.clientY)
    }
</script>
<script>
    // 默认事件
    var aBtn = document.querySelector('a');
    aBtn.onclick = function (ev) {
        ev.cancelBubble = true;
        console.log("a被点击了");
        // 手动转跳页面
        open('https://www.oldboyedu.com', '_self');
        // a标签默认会完成转跳, 如果取消默认事件
        return false;
    }

</script>
</html>

再补充一些

默认操作 具体指的是什么呢?
(1)把单击事件处理程序注册到一个锚元素,而不是一个外层的<div>上,那么就要面对另外一个问题:当用户单击链接时,浏览器会加载一个新页面。

(2)当用户在编辑完表单后按下回车键时,会触发表单的submit事件,在此事件发生后,表单提交才会真正发生。
这种行为与我们讨论的事件处理程序不是同一个概念,它是单击标签元素的默认操作。

如果我们不希望执行这种默认操作,那么在事件对象上调用.stopPropagation()方法也无济于事,因为默认操作不是在正常的事件传播流中发生的
在这种情况下,处理方法有:

1、w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
preventDefault它是事件对象(Event)的一个方法,作用是取消一个目标元素的默认行为。既然是说默认行为,当然是元素必须有默认行为才能被取消,如果元素本身就没有默认行为,调用当然就无效了。什么元素有默认行为呢?如链接<a>,提交按钮<input type=”submit”>等。当Event 对象的 cancelable为false时,表示没有默认行为,这时即使有默认行为,调用preventDefault也是不会起作用的。
1 <a href="http://www.cnblogs.com/yycode/" id="testA" >caibaojian.com</a>

var a = document.getElementById("testA");
a.onclick =function(e){
    if(e.preventDefault){
        e.preventDefault();//
    }else{
        window.event.returnValue = false;//IE
     //注意:这个地方是无法用return false代替的 
     //return false只能取消元素
    }
}


2、return false;
javascript的return false只会阻止默认行为,而是用jQuery的话则既阻止默认行为又防止对象冒泡

常见事件

鼠标事件

var box = document.querySelector('.box');

1. 点击事件
box.onclick = function () {
    console.log("单击");
};
2. 双击事件(应用场景不广)
box.ondblclick = function () {
    console.log("双击");
};
3. 鼠标右键
box.oncontextmenu = function () {
    console.log("右键了");
    return false;
};
4. 鼠标悬浮 | 移动 | 按下 | 抬起 | 离开
box.onmouseover = function () {
    console.log("悬浮");
};
box.onmousemove = function () {
    console.log("移动");
};
box.onmousedown = function () {
    console.log("按下");
};
box.onmouseup = function () {
    console.log("抬起");
};
box.onmouseout = function () {
    console.log("离开");
}

事件参数ev
ev.clientX:点击点X坐标
ev.clientY:点击点Y坐标

over | out   VS   enter | leave

总结:
1. 将子级与父级分开考虑, 大家都有各自的悬浮离开事件, 采用 over | out 组合
2. 将子级纳入父级考虑范围, 也就是只有父级去相应悬浮离开事件, 采用 enter | leave 组合
3. 单独考虑一个盒子的悬浮离开事件, 两套均可以

特性
从父级移至子级, 会触发out事件, 紧接着触发子级的over事件, 并可以冒泡给父级

从父级移至子级, leave事件并不会触发, 它认为子级是属于父级的一部分, enter事件, 也不会再次触发

悬浮子级:
sub over => sup over  支持冒泡
sup enter => sub enter  不支持冒泡

键盘事件

键盘事件
onkeydown:键盘按下会触发,长按持续触发
onkeyup:键盘抬起

事件参数ev

ev.keyCode:按键编号
ev.altKey:alt特殊按键
ev.ctrlKey:ctrl特殊按键
ev.shiftKey:shift特殊按键


<head>
    <meta charset="UTF-8">
    <title>键盘事件</title>
    <style>
        .box {
             100px;
            height: 100px;
            background: orange;
            position: absolute;
            top: 0;
            left: 0;
        }
    </style>
</head>
<body>
    <div class="box"></div>
</body>
<script>
    // 键盘长按会一直触发按下事件
    document.onkeydown = function (ev) {
        console.log(ev);
        // 按下的键盘编号
        console.log("按下", ev.keyCode);
        // console.log(ev.which);
    }
    document.onkeyup = function (ev) {
        console.log("抬起", ev.keyCode);
    }

    // 左上右下: 37-40
    var box = document.querySelector('.box');
    document.onkeydown = function (ev) {
        switch (ev.keyCode) {
            case 37:
                box.style.left = box.offsetLeft - 10 + 'px';
                break;
            case 38:
                box.style.top = box.offsetTop - 10 + 'px';
                break;
            case 39:
                box.style.left = box.offsetLeft + 10 + 'px';
                break;
            case 40:
                box.style.top = box.offsetTop + 10 + 'px';
                break;
        }
    }
</script>
</html>

表单事件

onfocus:获取焦点
onblur:失去焦点
onselect:文本被选中
oninput:值改变
onchange:值改变,且需要在失去焦点后才能触发
onsubmit:表单默认提交事件

文档事件

文档事件由window调用
onload:页面加载完毕触发
onbeforeunload:页面退出或刷新警告,需要设置回调函数返回值,返回值随意

图片事件 

onerror:图片加载失败

页面事件

onscroll:页面滚动
onresize:页面尺寸调整
window.scrollY:页面下滚距离

文档页面事件运用实例

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>其他事件</title>
    <script>
        // 当页面加载完毕之后再回调
        window.onload = function () {
            var box = document.querySelector('.box');
            console.log(box);
        }

        // 页面滚动可以设置给 window | document
        var isShow = false;
        window.onscroll = function () {
            console.log(window.scrollY);
            if (window.scrollY >= 1200) {
                if (!isShow) {
                    console.log("将返回Top按钮显示");
                    isShow = true;
                }
            } else  {
                if (isShow) {
                    isShow = false;
                }
            }
        }
    </script>
</head>
<body>
    <div class="box"></div>
    <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</body>

</html>

JS选择器

getElement系列

通过id名获取唯一满足条件的页面元素

document.getElementById('id名');
该方法只能由document调用
任何HTML元素可以有一个id属性,在文档中该值必须唯一
若浏览器中出现多个id名的情况,CSS样式对所有该id名的元素都生效,但javascript脚本仅对第一个出现该id名的元素生效。
getElementById()该方法接收一个参数:要取得元素的id,若找到则返回该元素,若不存在则返回null
注意:document.getElementById方法的内部实现需要用到this,这个this本来被期望指向document

跨浏览器兼容
1:在ie7中,使用getElementById()的时候,接收的参数id不区分大小写。
2:在表单元素中,如果表单设置有name属性,其name属性会被当做id识别出来。
3:id是唯一的,但name属性并不是唯一的。具有该名称的隐式全局变量会引用一个类数组对象,包括所有该命名的元素

通过class名获取所有满足条件的页面元素

document.getElementsByClassName('class名');
该方法可以由document及任意页面元素对象调用
document.getElementsByClass("class1
class2")可以拿到同时拥有class1和class2的元素,中间用空格隔开,不区分class1和class2的顺序。

兼容性:IE8及其以下版本的浏览器未实现getElementsByClassName方法
返回值为HTMLCollection (一个类数组结果的对象,使用方式同数组)
没有匹配到任何结果返回空HTMLCollection对象 ([]) 

通过name属性获取元素

getElementsByName(返回值是一个nodeList集合(区别于Array),可作用于Dom元素)
getElementsByName()方法接收一个参数,即要取得元素的name值。

document.getElementsByName("Name");Name为要获取元素的name属性值,这个方法一般适用于提交表单数据,当
元素为form、img、iframe、applet、embed、object的时候设置name属性时,会自动在Document对象中创建以该
name属性值命名的属性。
所以可以通过document.domName引用相应的dom对象。

在ie9中,getElementsByName()只对表单元素起作用。
IE9-浏览器中使用getElementsByName()方法也会返回id属性匹配的元素。因此,不要将name和id属性设置为相同的值。

通过tag名获取所有满足条件的页面元素

document.getElementsByTagName('tag名');
该方法可以由document及任意页面元素对象调用
返回值为HTMLCollection (一个类数组结果的对象,使用方式同数组)
没有匹配到任何结果返回空HTMLCollection对象 ([]) 
可以使用方括号语法或item()方法来访问类数组对象中的项,length属性表示对象中元素的数量。
[注意]通过getElementsByTagName()方法取得的类数组对象有一个namedItem()方法,可以通过元素的name属性取得集合中的第一个值。safari和IE不支持该方法。document.getElementsByTagName("*")表示匹配文档的所有元素

querySelect系列

获取第一个匹配到的页面元素

document.querySelector('css语法选择器');
该方法可以由document及任意页面对象调用 

获取所有匹配到的页面元素

document.querySelectorAll('css语法选择器');
该方法可以由document及任意页面对象调用
返回值为NodeList (一个类数组结果的对象,使用方式同数组)
没有匹配到任何结果返回空NodeList对象 ([])

id名

可以通过id名直接获取对应的页面元素对象,但是不建议使用

js选择器运用实例

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>js选择器</title>
    <style>
        #d {
            color: red;
        }
    </style>
</head>
<body id="bd">
    <!--注释-->
    <div id="d" class="dd">我是ddd111</div>
    <div id="d" class="dd">我是ddd222</div>

    <div class="sup1">
        <div id="div"></div>
    </div>
    <div class="sup2">
        <div id="div"></div>
    </div>
</body>
<script>
    // 节点(了解): 在文档(document)中出现的所有内容都是document中的节点
    // 节点(node): 标签节点(元素element) | 注释节点 | 文本节点 | <!doctype>节点
    console.log([bd]) // 文本 注释 文本 标签 文本 标签 文本 标签 文本 标签 文本
</script>
<script>
    // js选择器: 将js与html建立起连接
    // js中一般称标签为页面元素

    // 1.直接通过id名进行匹配
    console.log(d);  // 两个都可以找到

    // 2.getElement系列(最严谨)
    // 所有显示在页面中的内容(展现给用户看的), 都是属于文档(document)对象的内容, 存放在文档中
    // console.log(document)
    // 获取文档中的标签 => document对象通过.语法去获取具体的目标标签元素
    // ① id
    var div = document.getElementById('d');  // 检索得到页面中出现的第一个满足条件的目标
    console.log(">>>", div);

    var body = document.getElementById("bd");
    console.log(body);

    // 注: getElementById该方法只能由document来调用
    // 原因: 我们需要保证一个文档中一个id只能出现一次, document检索的就是文档,
    // 而某父级标签只能检索自身标签内部区域, document可以保证文档中只能一个id
    // 只出现一次,某父级标签只能保证自身内部区域id不重复,能不能保证与外界不重复?
    // 不能, 所以从安全角度出发, 获取唯一对象的getElementById方法只能由能确定唯一id的
    // 对象来调用, 能被document调用, 不能被sup来调用

    // ② 类名
    var divs = document.getElementsByClassName('dd');
    console.log(divs);
    // 两个div在body之中, 上方已经获取了body, 那能否通过body来获取body中的div
    var divs1 = body.getElementsByClassName('dd');
    console.log(divs1);

    // ③ 标签名
    var divs = document.getElementsByTagName('div');
    console.log(divs)

    console.log('--------------------------------------------------');

    // 3.querySelector系列(最方便)
    // 参数: 就是css选择器语法
    // querySelector检索第一个
    var div = document.querySelector('body > .dd');
    console.log(div);

    // querySelectorAll检索所有满足结果
    var divs = document.querySelectorAll('body > .dd');
    console.log(divs);
    var divs = body.querySelectorAll('.dd');
    console.log(divs)

    var divs = body.querySelectorAll('#d');  // 不严谨
    console.log(divs)


</script>
</html>

选择器分类

1. ID选择器

使用ID选择器时,需在前面添加“#”,区分大小写,语法如下:document.querySelector('#id'); //等同于document.getElementById('id')

2. 元素选择器

元素选择器通过指定的标签查询元素,此时querySelectorAll等同于getElementsByTagName,语法如下:document.querySelectorAll('a'); //获取页面上的所有a元素并返回元素

3. 样式类选择器

使用元素的样式类获取一个或一类元素,样式名字前使用“.”(英文句号)开头,语法如下:document.querySelectorAll('.btn'); //获取所有样式类中包含btn类名的元素

4. 分组选择器

使用querySelectorAll不仅可以获取一个或一类元素,还可以同时获取其他类别元素,两种类型之间使用逗号隔开,语法如下:document.querySelectorAll('a,p'); 
//获取页面上所有a元素和p元素,并通过一个列表返回document.querySelectorAll('.btn,.txt'); //获取页面上所有包含btn和txt样式类名的元素

5. 属性选择器

获取页面上包含指定属性的元素,属性名称可以是元素原生属性和用户自定义属性,语法如下:document.querySelectorAll('a[target="_blank"]'); 
//获取页面上所有target属性为_blank的a元素document.querySelectorAll('img[data-id]'); //获取页面上所有带有自定义属性data-id的img元素

6. 后代选择器

主要用于选择作为某元素后代的元素,规则左边的选择器一端包含两个或多个用空格分隔的选择器,如div a可以理解为查找所有被div包围的所有a元素,语法如下:document.querySelectorAll('div a'); 
//获取页面上所有被div包含的a元素document.querySelectorAll('div .btn'); //获取页面上所有被div包含的带有btn样式类名的元素
 
7. 子元素选择器

后代选择器会将元素底下的所有相关元素都搜索出来,如果想进一步缩小范围,可以使用子元素选择器,只会选择某个元素的一级子元素,子元素用“>”(大于号)表示,代码如下:
<html>    <div id="first">        <div></div>        <div></div>    </div></html>     <script>    document.querySelectorAll('html>div'); //只返回一个id为first的div元素</script>
 
8. 相邻兄弟选择器(比较少用)

选择紧接在另一个元素后的元素,而且两者有相同的父元素,相邻兄弟选择器使用“+”(加号),代码如下:
<div>    <div></div>    <div></div></div><p id="p1"></p><p id="p2"></p><script>    document.querySelectorAll('div+p'); 
//只返回一个id为p1的p元素</script>
 
9. 伪类选择器

“:first-child”表示选择元素的第一个子元素,“:last-child”表示选择元素的最后一个子元素,“:nth-child(n)”表示选择元素的第n个子元素。“:first-child”的使用例子,代码如下:
<div>    <p id="p1"></p>    <p id="p2"></p></div> <script>    document.querySelectorAll('p:first-child'); //只返回一个id为p1的p元素</script>

闭包

function outer() {
var data = {}
function inner() {
return data;
}
return inner;
}

闭包目的:不允许提升变量作用域时,该函数的局部变量需要被其他函数使用
闭包本质:函数的嵌套,内层函数称之为闭包
闭包的解决案例:①影响局部变量的生命周期,持久化局部变量;②解决变量污染

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>闭包</title>
</head>
<body>
闭包
</body>
<script>
    // 了解知识点
    // 闭包: 局部的函数(被一个函数包裹的函数)
    // 为什么使用闭包:
    // 1.一个函数要使用另一个函数的局部变量
    // 2.闭包会持久化包裹自身的函数的局部变量
    // 3.解决循环绑定

    // 函数的嵌套定义
    function outer() {
        var num = 10;
        function inner() {
            // 1.在inner函数中,使用了outer的局部变量num
            return num;
        }
        return inner;
    }
    var innerFn = outer();
    // 2.借助闭包,将局部变量num的生命周期提升了
    var num = innerFn();
    console.log(num);


</script>
</html>

循环绑定

.html文件

<ul>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>

.js文件

var lis = document.querySelector('li');
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
// 打印列表项的索引
console.log(i);
}
}

会发生变量污染

解决方法

1 获取局部作用域解决   2  闭包解决  3  对象属性解决

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>循环绑定</title>
</head>
<body>
<div class="box">0000000000000000001</div>
<div class="box">0000000000000000002</div>
<div class="box">0000000000000000003</div>
</body>
<script>
    var divs = document.querySelectorAll(".box");
    /* 存在污染
    for (var i = 0; i < divs.length; i++) {
        // i = 0 | 1 | 2 | 3
        // 循环绑定
        divs[i].onclick = function () {
            console.log("***", i)
        }
    }
    // i = 3
    console.log(">>>", i);
    */

    /* 利用块级作用域解决
    for (let i = 0; i < divs.length; i++) {
        // {i=0 <= i} {i=1 <= i} {i=2 <= i}
        // i = 3
        // 循环绑定
        divs[i].onclick = function () {
            console.log("***", i)
        }
    } // for运行结束, i=3会被销毁
    console.log(">>>", i)
    */

    // 利用标签的属性解决
    /*
    for (var i = 0; i < divs.length; i++) {
        divs[i].index = i;
        divs[i].onclick = function () {
            // console.log("###", i)
            console.log(this.index)
        }
    }
    */

    // 利用闭包处理循环绑定
    for (var i = 0; i < divs.length; i++) {
        (function () {
            var index = i;
            divs[index].onclick = function () {
                console.log("###", index)
            }
        })()
        /*
        (function (index) {
            divs[index].onclick = function () {
                console.log("###", index)
            }
        })(i)
         */
        /*
        (function (i) {
            divs[i].onclick = function () {
                console.log("###", i)
            }
        })(i)
         */
    }


</script>
</html>

面向对象JS

属性与方法

var obj = {}; | var obj = new Object();

属性

obj.prop = "";

方法

obj.func = function () {}

删除属性与方法

delete obj.prop
delete obj.func

属性和方法实际使用

对象: 特征与行为的结合体, 是一个具象的实体

// js对象语法
var obj = {
// 属性
name: 'Zero',
// 方法
teach: function () {
 console.log("教学");
}
};
// 对象使用属性与方法, 采用.语法
console.log(obj.name);
obj.teach();

JS中中括号([ ])与 .

中括号运算符总是能代替点运算符。但点运算符却不一定能全部代替中括号运算符
中括号运算符可以用字符串变量的内容作为属性名。点运算符不能
中括号运算符可以用纯数字为属性名。点运算符不能
中括号运算符可以用js的关键字和保留字作为属性名。点运算符不能

下面的例子中,a.b表示对象a的属性b,既可以设置也可以读取

object[key]=>key为常量时,object[key]等价于object.key,例如:a.b == a['b'] 

特殊的,object[key] 中key为变量时,只能用中括号形式

var a = {};
 // a.b = 1; 
a['b'] = 1; 
c = a.b; 
// c = a['b'];
alert(c);

标识符是否合法对两种用法的影响

var obj = {}; 
// 为obj添加一个属性name,name是合法的标识符,即也可以通过obj.name方式来定义

obj['name'] = 'jack'; 

// 为obj添加一个属性2a,2a不是合法的标识符(不能以数字开头),不能通过obj.2a来定义

obj['2a'] = 'test';

var obj = {name:'jack'};
obj['2a'] = 'test';
obj['name']; // --> jack 
obj['2a']; // --> test (不能通过obj.2a获取)

总结:常量用点,变量就用中括号

运算符左侧应当是一个表达式,它返回一个对象。
对于点(.)来说,右侧必须是一个以属性名称命名的简单标识符。
对于方括号([])来说,方括号里必须是一个计算结果为字符串的表达式,这个字符串就是属性的名字

当通过点运算符(.)方法对象属性时,属性名用一个标识符来表示。标识符必须直接出现在js程序中,它们不是数据类型,因此程序无法修改它们。
反过来讲,当通过[]来方式对象的属性时,属性名通过字符串表示。字符串是js的数据类型,在程序运行时可以修改和创建它们

<script type="text/javascript">
var addr = "";
for(i=0;i<4;i++){
addr += cutomer["address" + i] + "
";
}
</script>

这段代码读取customer对象的address0,adddress1,address2,adddress3属性,并将他们连接起来

类字典结构使用

结构

var dict = {name: "zero", age: 18}

拓展

var dict = {"my-name": "zero", fn: function () {}, fun () {}}

使用

dict.name | dict["my-name"] | dict.fn()

构造函数(ES5)

// 构造函数: 声明与普通函数一样, 只是函数名采用大驼峰命名规则
function Person(name) {  // 类似于python中的类一样来使用
   // 构造函数内部属性方式不同于普通函数
   this.name = name;  // this代表Person构造函数实例化出的所有具体对象中的某一个
   this.teach = function () {
       console.log(this.name + "正在教学");
   }
}
// 如何使用构造函数中的属性与方法
// 1. 通过构造函数实例化出具体对象
// 2. 通过对象.语法调用属性与方法
var p1 = new Person("杨虎虎");  // name: 杨虎虎
var p2 = new Person("刘xx");  // name: 刘xx
console.log(p1.name);
console.log(p2.name);
p1.teach();
p2.teach();


为什么要用new?

new  会创建  pre 的对象,
但是没有 new 的话就没有没有可返回的值或对象了,所以是  undefined。
如果不想new 新的对象的话,可以在 pre 的方法里 返回一个值或对象。
function pre(){
    this.radius = Math.random();
    return this.radius;
}

加 new 会把这个函数当作是一个构造器,返回一个对象。
不加的话,就是调用一个普通的函数,结果视函数返回值而定。 
有new,就返回对象,不管函数返回值如何。
无new,就看函数返回值。
构造函数最好返回 this 或者无返回值, 否则容易混淆。

类及继承(ES6)

// ES6
// 引入了类
class Student {  // 类, 可以实例化对象, 但实例化出的对象需要加以区分
// 需要构造器(构造函数)来完成对象的声明与初始化
// ES6规定方法的语法
constructor (name) {
   // 属性在构造器中声明并完成初始化
   this.name = name;
}
// 类中规定普通方法
study () {
   console.log(this.name + "正在学习");
}
// 类方法
static fn() {
console.log("我是类方法")
  }
}
// 1.实例化类的对象
let stu1 = new Student("嘿嘿");
// 2.使用属性与方法
console.log(stu1.name);
stu1.study();

let stu2 = new Student("嘻嘻");
console.log(stu2.name);
stu2.study();

Student.fn()

<script>
// 类方法
class Tool { // 功能类(工具类)中的方法都定义为类方法
static max (num1, num2) {
return num1 > num2 ? num1 : num2;
}
}
// 通过Tool类来求两个数中的大值, 需要Tool类的对象出现吗? 不需要 => 功能有类直接使用
console.log(Tool.max(666, 888));


// throw "自定义异常";
// console.log("上面如果出现了异常, 逻辑将会被强制停止");
// var num = 10 / 0;
// console.log(num)
</script>