DOM
1、简介
- DOM,全称Document Object Model文档对象模型
- js中通过DOM来对html文档进行操作。
- 文档:
- 文档表示的就是整个html网页文档
- 对象:
- 对象表示将网页中的每一个部分都转换为了一个对象
- 模型:
- 使用模型来表示对象之间的关系,这样方便我们获取对象
a、节点
节点:Node — 构成html文档最基本的单元
常用节点分为四类:
- 文档节点:整个html文档
- 元素节点:html文档中的html标签
- 属性节点:元素的属性
- 文本节点:html标签中的文本内容
所有节点都有的三个属性:nodeName、nodeType、nodeValue
<ul id="myul"> <li id="myli" class="action" name="myli">中国</li> </ul> <script> var lis = document.getElementById("myli"); var lisTest = lis.firstChild; console.log(lisTest.nodeValue) //中国 </script>
2、文档的加载
浏览器在加载一个页面时,是按照自上向下的顺序加载的,读取到一行就运行一行,如果将script标签写到页面的上边,在js代码执行完时,页面还没有加载,页面没有加载DOM对象也没有加载会导致无法获取到DOM对象,如果这时在script标签中有获取DOM对象的,或是有事件绑定的,则会报错,代码展示如下:
<script>
var but = document.getElementById("but"); // 因为页面还没有加载,也就是没有button标签,这里又是获取button标签对象的,所以返回的会是null
// 而null是不能添加属性的,所以下面的代码出错
but.onclick = function (){
alert("点击了")
}
</script>
<body>
<button id="but" >按钮</button>
</body>
// 会报错:TypeError: Cannot set property 'onclick' of null
但是我们头铁,一定要把script标签写在页面的上面,那么可以使用一个事件:onload。表示在页面或图像加载完成后立即发生。
支持onload事件的js对象有:window、image、layer
<script>
window.onload = function() {
// alert("点击了")
var but = document.getElementById("but");
but.onclick = function() {
alert("点击了")
}
}
</script>
<body>
<button id="but">按钮</button>
</body>
3、获取、设置属性值
a、获取
在js中,想要获取到一个元素对象的属性值,很简单,直接元素对象.属性名
语法:元素对象.属性名
<script>
window.onload = function() {
// alert("点击了")
var but = document.getElementById("but");
but.name; // myname
but.id; // but
but.class; // undefined
}
</script>
<body>
<button class="myclass" name="myname" id="but">按钮</button>
</body>
通过上面的例子可以知道元素对象.属性名
能获取到属性的值,但是有一个特例,就是class,通过元素对象.属性名
的方式是不能获取到class属性的值得,因为class在js中是保留字,所以在这里不起作用了
要想获取class属性的值,可以通过下面的方式获取:
but.className; // myclass
b、设置属性值
和获取属性值一样,都是很简单的操作,语法:元素对象.属性名=属性值
<body>
<button class="myclass" name="myname" id="but">按钮</button>
</body>
<script>
var but = document.getElementById("but");
but.name = "Chage"; // 这样name属性的值就修改了
</script>
4、获取document文档中的节点
在js中,有提供了方法让我们直接获取到节点:
使用document作为调用者,调用过去节点的方法,这样是在整个文档中查找的。
a、通过id 获取到节点
document.getElementById()
通过id属性获取一个元素节点对象
用法实例:
<body> <button id="but">按钮</button> </body> <script> var but = document.getElementById("but"); console.log(but.innerHTML) </script>
b、通过标签名 获取到节点
document.getElementsByTagName()
通过标签名获取一组元素节点对象
这个方法会返回一个类数组对象,所有查询到的元素都会封装到对象中。
<body> <button id="but">按钮1</button> <button id="but">按钮2</button> </body> <script> var but = document.getElementsByTagName("button"); console.log(but[0].innerHTML) console.log(but.length) // 2 </script>
c、通过name属性 获取到节点
document.getElementsByName()
通过name属性获取一组元素节点对象
<script> window.onload = function() { var but = document.getElementsByName("myname"); console.log(but[0].innerHTML) } </script> <body> <button name="myname" id="but">按钮</button> </body>
d、通过class属性 获取到节点
document.getElementsByClassName()
通过class属性获取一组元素节点对象
注意:这个方法有兼容的局限性,IE浏览器有IE8以上才支持。
<body> <button name="myname" class="myclass" id="but">按钮</button> </body> <script> var but = document.getElementsByClassName("myclass"); console.log(but[0].innerHTML) </script>
5、获取元素节点的子节点
通过具体的元素节点调用:
- getElementsByTagName()
- 方法,返回当前节点的指定标签后代节点
- getElementsByClassName()
- 方法,返回当前节点的指定标签后代节点
- childNodes
- 属性,表示当前节点的所有子节点.
- 注意:
- 该属性会获取包括文本节点在内的所有节点,并且根据DOM标准,标签之间的空白也会当成文本节点。
- 在IE8及以下的浏览器中,不会将空白文本当成子节点,所有该属性会返回不包括空白节点的所有子节点。
- children
- 属性,获取当前元素的所有子元素(就是标签了,不包括标签之间的空白文本),推荐使用这种,所有的浏览器都兼容。
- firstChild
- 属性,表示当前节点的第一个子节点,包括标签之间的空白文本子节点,若当前节点和下一个元素之间有空白子节点,则该属性就表示为那个空白子节点。
- firstElementChild
- 属性,表示当前节点的第一个元素(注意IE有版本要求(IE8以上))
- lastChild
- 属性,表示当前节点的最后一个子节点,和firstChild属性一样,只不过获取的是最后一个子节点。
1)、getElementsByTagName()
<ul id="myul">
<li class="action"name="myli">中国</li>
<li class="action" >广东</li>
<li class="action">广州</li>
<li class="action">花都</li>
</ul>
<script>
var ulId = document.getElementById("myul");
var lis = ulId.getElementsByTagName("li");
var lis = ulId.getElementsByClassName("action")
console.log(lis) // HTMLCollection{...}
console.log(lis.length) // 4
for(var i=0;i<lis.length;i++){
console.log(lis[i].innerHTML) // 中国 // 广东 // 广州 // 花都
}
</script>
2)、childNodes
<ul id="myul">
<li class="action"name="myli">中国</li>
<li class="action" >广东</li>
<li class="action">广州</li>
<li class="action">花都</li>
</ul>
<script>
var ulId = document.getElementById("myul");
var lis = ulId.childNodes;
console.log(lis) // NodeList{...}
console.log(lis.length) // 9
for(var i=0;i<lis.length;i++){
console.log(lis[i].innerHTML) // undefined // 中国 // undefined // 广东 // undefined // 广州 // undefined // 花都 // undefined
}
</script>
3)、children
<ul id="myul">
<li class="action"name="myli">中国</li>
<li class="action" >广东</li>
<li class="action">广州</li>
<li class="action">花都</li>
</ul>
<script>
var ulId = document.getElementById("myul");
var lis = ulId.childNodes;
console.log(lis) // HTMLCollection{...}
console.log(lis.length) // 4
for(var i=0;i<lis.length;i++){
console.log(lis[i].innerHTML) // 中国 // 广东 // 广州 // 花都
}
</script>
4)、firstChild
<ul id="myul">
<li class="action"name="myli">中国</li>
<li class="action" >广东</li>
<li class="action">广州</li>
<li class="action">花都</li>
</ul>
<script>
var ulId = document.getElementById("myul");
var lis = ulId.firstChild;
console.log(lis) // <li class="action"name="myli">中国</li>
console.log(lis.innerHTML)// 中国
</script>
6、获取元素的父节点
通过具体的节点调用:
- parentNode
- 属性,表示当前节点的父节点(也就是元素了,因为父节点只有一个,并且是元素)
<ul id="myul">
<li id="myli" class="action"name="myli">中国</li>
<li class="action" >广东</li>
<li class="action">广州</li>
<li class="action">花都</li>
</ul>
<script>
var lis = document.getElementById("myli");
var ul = lis.parentNode;
console.log(ul) // HTMLUListElement]
console.log(ul.innerHTML)// <li.....</li>
</script>
7、获取元素的兄弟节点
通过具体的节点调用:
- previousSibling
- 属性,表示点前节点的前一个兄弟节点(非元素,所以也有可能获取到空白节点,如果标签之间有空白节点的话)
- previousElementSibling
- 属性,获取前一个兄弟元素,(注意IE有版本要求(IE8以上))
- nextSibling
- 属性,表示当前节点的后一个兄弟节点
<ul id="myul">
<li class="action" >地球</li>
<li id="myli" class="action"name="myli">中国</li>
<li class="action" >广东</li>
</ul>
<script>
var lis = document.getElementById("myli");
var lis2 = lis.previousElementSibling;
console.log(lis2) // HTMLUListElement]
console.log(lis2.innerHTML)// <li.....</li>
</script>
8、获取元素的内容(文本节点)
a、innerHTML
- 该属性可以获取到元素内部的文本内容
- 要注意的是不会自动的将html标签去除,同样将标签输出
- 用方法:节点对象.innerHTML
b、innerText
- 该属性可以获取到元素内部的文本内容
- 它和innerHtml类似,不同的是它会自动将html标签去除
- 使用方法:节点对象.innerText
9、获取DOM节点的其他方式
a、获取body节点
document.body
// var body = document.getElementsByTagName("body")[0]; var body = document.body console.log(body)
b、获取html节点
document.documentElement
var html = document.documentElement; console.log(html)
c、获取文档中的所有元素
document.all 或 document.getElementsByTagName("*")
var all = document.all console.log(all.length) for(var i=0;i<all.length;i++){ console.log(all[i]) }
获取文档中的所有元素,有一个元素算一个
10、根据选择器获取元素节点对象
a、querySelector()
document.querySelector()
需要一个选择器的字符串作为参数,可以根据一个css选择器来查询一个元素节点对象。
IE8及以上版本都能支持。IE8不支持
document.getElementsByClassName()
方法,但是可以使用这个方法代替,并且这个方法很强大。使用该方法总会返回唯一的一个元素,如果满足条件的元素有多个,那么它只会返回第一个元素.
<div class="box"> <div>我是第二个div</div> </div> <div class="box"> <div>我是第二个div</div> </div> <script> var div = document.querySelector(".box"); console.log(div.innerHTML) var div = document.querySelector(".box div"); console.log(div.innerHTML) </script>
b、querySelectorAll()
document.querySelectorAll()
该方法和querySelector()方法用法类似,不同的是它会将符合条件的元素封装到一个数组中返回。即使符合条件的元素只有一个,也会返回数组.
<div class="box"> <div>我是第二个div</div> </div> <div class="box"> <div>我是第二个div</div> </div> <script> var div = document.querySelectorAll(".box"); console.log(div.length) </script>
------- 子节点增删改 ------
1、创建子节点
a、创建元素节点
document.createElement()
需要一个标签名作为参数,将会根据标签名创建元素节点对象,并将创建好的对象作为返回值返回。
// 例如要创建一个<li>标签 var li = document.createElement("li"); alert(li)
b、创建文本节点
document.createTextElement()
需要一个文本内容作为参数,经会根据该内容创建文本对象,并将新的节点返回
// 例如创建一个内容为“中国的家”的文本节点 var text = document.createTextNode("中国的家") // alert(text.nodeValue); // alert(text.data); console.log(text)
2、添加子节点
使用 innerHtml属性可以向节点添加内容:
<ul>
</ul>
<script>
var ul = document.getElementsByTagName("ul")[0];
// 例如要添加"北金"
ul.innerHTML += "<li>北金</li>"
</script>
c、向符节点添加新的子节点
语法:符节点.appendChild(子节点)
向一个符节点添加一个新的子节点,
注意:但是是添加在父节点的子节点列表的末尾
<ul> </ul> <script> var ul = document.getElementsByTagName("ul")[0]; // 例如要创建一个<li>标签 var li = document.createElement("li"); var text = document.createTextNode("中国的家") li.appendChild(text) ul.appendChild(li) </script>
第二种用法:使用 appendChild() 方法移除元素到另外一个元素。
**提示:**如果文档树中已经存在了 newchild,它将从文档树中删除,然后重新插入它的新位置。如果 newchild 是 DocumentFragment 节点,则不会直接插入它,而是把它的子节点按序插入当前节点的 childNodes[] 数组的末尾。
var node=document.getElementById("myList2").lastChild; document.getElementById("myList1").appendChild(node);
d、在指定的子节点前面插入新的子节点
语法:element.insertBefore()
这个方法需要两个参数,一个是新的子节点,一个是新节点要确定在哪个子节点前面插入的子节点。
<ul> <li id="bj">北京</li> </ul> <script> // 例如要在北京前边添加一个新的子节点 var ul = document.getElementsByTagName("ul")[0]; var bj = document.getElementById("bj"); var li = document.createElement("li"); var text = document.createTextNode("中国的家") li.appendChild(text) ul.insertBefore(li, bj) </script>
第二种用法:使用 insertBefore 方法来 插入/移除 已存在的元素。
提示: 如果你想创建一个新的文本列表项,在 LI 元素后你应该添加元素的文本节点,然后在列表中添加 LI元素。你也可以使用 insertBefore 方法来 插入/移除 已存在的元素。
var node=document.getElementById("myList2").lastChild; var list=document.getElementById("myList1"); list.insertBefore(node,list.childNodes[0]);
e、使用属性和方法的区别
使用innerHtml属性添加子节点,过程都是先删除父节点的说有子节点,然后再把所有的子节点添加进来(包括旧的和新的子节点),这样有一个坏处,就是旧的子节点绑定的事件啊什么都会失效,所以要慎用这种方式。
但是使用appendChild() 或 childNodes() 方法添加子节点,发生变化的只是新添加的子节点,其他的子节点没有发生任何的变化。
<ul> <li id="bj"> 北京 </li> </ul> <script> var bj = document.getElementById("bj"); bj.onclick = function() { var ul = document.getElementsByTagName("ul")[0]; // 例如要创建一个<li>标签 var li = document.createElement("li"); var text = document.createTextNode("中国的家") li.appendChild(text) ul.appendChild(li) } bj.onclick = function() { var ul = document.getElementsByTagName("ul")[0]; ul.innerHTML += "<li>...</li>" } </script>
两种方式结合:
<ul> </ul> <script> var ul = document.getElementsByTagName("ul")[0]; // 例如要创建一个<li>标签 var li = document.createElement("li"); li.innerHtml = "中国的家" ul.appendChild(li) </script>
3、替换子节点
a、element.replaceChild()
语法:node.replaceChild(newnode,oldnode)
参数 类型 描述 newnode Node 对象 必须。你要插入的节点对象。 oldnode Node object 必须。你要移除的节点对象。 新节点可以是文本中已存在的,或者是你新创建的。
<ul> <li id="bj"> <ol> <li> 123 </li> </ol> </li> </ul> <script> // 例如要在北京前边添加一个新的子节点 var ul = document.getElementsByTagName("ul")[0]; var bj = document.getElementById("bj"); var li = document.createElement("li"); var text = document.createTextNode("中国的家") li.appendChild(text); ul.replaceChild(li, bj); </script>
4、删除子节点
a、node.removeChild(node)
参数 类型 描述 node 节点对象 必须。 你要移除的节点对象。 removeChild() 方法可从子节点列表中删除某个节点。
如删除成功,此方法可返回被删除的节点,如失败,则返回 NULL。
<ul> <li id="bj"> 中国好声音 </li> </ul> <script> // 例如要在北京前边添加一个新的子节点 var bj = document.getElementById("bj"); bj.parentNode.removeChild(bj); </script>
5、克隆子节点
node.cloneNode(deep)
参数 类型 描述 deep Boolean 可选。该方法将复制并返回调用它的节点的副本。如果传递给它的参数是 true,它还将递归复制当前节点的所有子孙节点。否则,它只复制当前节点。 cloneNode() 方法可创建指定的节点的精确拷贝。
cloneNode() 方法 拷贝所有属性和值。
该方法将复制并返回调用它的节点的副本。如果传递给它的参数是 true,它还将递归复制当前节点的所有子孙节点。否则,它只复制当前节点。
样式
1、操作内联样式
注意:通过style 属性设置和读取的都是内联样式。
1、设置
通过js修改元素的样式:
语法:元素.style.样式名 = 样式值
注意,样式值要是字符串。
<style> #box { width: 200px; height: 200px; background-color: red; } </style> <button>点击</button> <div id="box">sd</div> <script> document.getElementsByTagName("button")[0].onclick= function() { var box = document.getElementById("box"); box.style.width = "300px"; box.style.height = "300px"; } </script>
但是,如果样式的名字是用“-”连接起来的,继续使用上面的方式就不行了,因为在js中,把“-”解析成了运算符了。比如“background-color”
解决方法:将使用“-”连接的样式名改为驼峰命名法即可:去掉“-”,然后将“-”后的字母大写
<script> document.getElementsByTagName("button")[0].onclick= function() { var box = document.getElementById("box"); box.style.backgroundColor = "yellow"; } </script>
或者使用另一种方法:
在命名不符合规范的时候可以很好的使用,并且这种方式使用非常的灵活
<script> document.getElementsByTagName("button")[0].onclick= function() { var box = document.getElementById("box"); box.style["background-color"] = "yellow"; } </script>
2、获取
语法:元素.style.样式名
注意:通过style属性获取到的都是内联样式,无法读取到样式表或外联样式中的值
<style> #box { width: 200px; height: 200px; background-color: red; } </style> <button>点击</button> <div id="box">sd</div> <script> document.getElementsByTagName("button")[0].onclick= function() { var box = document.getElementById("box"); console.log(box.style.width) // 结果为空,因为box元素还没有内联样式 console.log(box.style.height) // 结果为空,因为box元素还没有内联样式 } </script>
2、只读当前渲染的样式
1、currentStyle
在上面的实例知道,如果一个元素的样式不是写在内联样式中的,是通过style属性来获取或设置元素样式是有一点局限性的。
现在,将通过一个新的方法,直接获取元素当前正在应用的样式,而不用管这个样式是内联样式或是其他的样式。方式如下;
获取样式语法:元素.currentStyle.styleName
<style> #box { width: 200px; height: 200px; background-color: red; } </style> <button>点击</button> <div id="box">sd</div> <script> document.getElementsByTagName("button")[0].onclick= function() { var box = document.getElementById("box"); console.log(box.currentStyle.width) console.log(box.currentStyle["width"]) } </script>
注意:可以取得内部和外部样式,但是只兼容ie浏览器,获取的样式只能读
2、getComputedStyle()
window.getComputedStyle(element, pseudoElement)
getComputedStyle() 方法用于获取指定元素的 CSS 样式。
获取的样式是元素在浏览器中最终渲染效果的样式。
参数说明:
- element: 必需,要获取样式的元素。
- pseudoElement: 可选,伪类元素,当不查询伪类元素的时候可以忽略或者传入 null。
返回值:
- 返回的对象是 CSSStyleDeclaration 类型的对象。
<style> #box { width: 200px; height: 200px; background-color: red; } </style> <button>点击</button> <div id="box">sd</div> <script> document.getElementsByTagName("button")[0].onclick= function() { var box = document.getElementById("box"); var style = getComputedStyle(box); console.log(style.width) console.log(style["width"]) style.height = "400px"; } </script>
注意:
- 除了IE要求是版本11以上才能支持,其他的浏览器都支持。
- 作用和currentStyle一样但是不适用于ie浏览器,会获得元素所有原始样式,即使这个元素什么样式也没有设置,其中第二种写法源于jQuery源码写法,是.css()的底层操作方法。
- 如果获取的样式没有设置,则会获取到真是的值,而不是默认值,比如:没有设置width,它不会获取到auto,而知一个长度。
3、兼容
function getStyle(element, property) {
return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(element, false)[property] :
element.currentStyle[property];
}
注意:关于 document.defaultView 的相关解释,点击这里
类的操作
1、一行代码修改多个样式
<style>
.box1{
width: 6.25rem;
height: 6.25rem;
background-color: #DEDEDE;
}
.box2{
width: 12.5rem;
height: 12.5rem;
background-color: #ded484;
}
</style>
<button>按钮</button>
<div class="box1" id="box1"></div>
<script>
var btn = document.getElementsByTagName("button")[0];
var box1 = document.getElementById("box1");
btn.onclick = function(){
// 修改一行代码,改变多个样式
box1.className = "box2";
}
</script>
这样一来,我么只需要修改一次,既可以同时修改多个样式看,浏览器只需要重新渲染页面一次,这样性能比较好,冰球这种方式,可以使表现和行为进一步的分离。
2、判断元素是否有指定类
// 判断一个元素中是否含有制定的class属性值
// obj:元素。cn:要查询的类名
functionhasClass(obj, cn){
var name = obj.className;
// 正则表达式 \b 标识单词边界
var reg = new RegExp("\\b" + cn + "\\b");
return reg.test(name);
}
3、添加类
// 向一个元素添加一个新的类
// obj:元素。cn:要添加的类名
function addClass(obj, cn){
if(!hasClass(obj, cn)){
obj.className += cn;
}
}
4、删除类
// obj:元素。cn:要添加的类名
function removeClass(obj, cn){
var reg = new RegExp("\\b" + cn + "\\b");
obj.className = obj.className.replace(reg, "");
}
5、切换类
如果元素中具有指定类,则删除。
如果元素中没有指定类,则添加。
// obj:元素。cn:要添加的类名
function toggleClass(obj, cn){
if(hasClass(obj, cn)){
removeClass(obj, cn)
}else{
addClass(obj, cn)
}
}
6、classList
classList 属性返回元素的类名,作为 DOMTokenList 对象。
该属性用于在元素中添加,移除及切换 CSS 类。
classList 属性是只读的,但你可以使用 add() 和 remove() 方法修改它。
属性:length:返回类列表中类的数量(只读)
方法 | 描述 |
---|---|
add(class1, class2, …) | 在元素中添加一个或多个类名。 如果指定的类名已存在,则不会添加 |
contains(class) | 返回布尔值,判断指定的类名是否存在。 可能值: true - 元素包已经包含了该类名 false - 元素中不存在该类名 |
item(index) | 返回元素中索引值对应的类名。索引值从 0 开始。 如果索引值在区间范围外则返回 null |
remove(class1, class2, …) | 移除元素中一个或多个类名。 注意: 移除不存在的类名,不会报错。 |
toggle(class, true|false) | 在元素中切换类名。 第一个参数为要在元素中移除的类名,并返回 false。 如果该类名不存在则会在元素中添加类名,并返回 true。 第二个是可选参数,是个布尔值用于设置元素是否强制添加或移除类,不管该类名是否存在。例如: 移除一个 class: element.classList.toggle(“classToRemove”, false); 添加一个 class: element.classList.toggle(“classToAdd”, true); 注意: Internet Explorer 或 Opera 12 及其更早版本不支持第二个参数。 |
语法:document.getElementById(“myDIV”).classList.add(“mystyle”);
注意:IE9及以下不支持该属性
元素常见属性
1、元素可见区宽高
element.clientHeight | 在页面上返回内容的可视高度(不包括边框,边距或滚动条) |
---|---|
element.clientWidth | 在页面上返回内容的可视宽度(不包括边框,边距或滚动条) |
这俩个属性的返回值都是不带单位(px)的,返回的都是数字值,可以直接进行计算。
注意:可见区包括内容区和内边距
两个属性都是只读的,不能用于设置值,因为这个属性是由内容区和内边距相加而成,要是设置值得话,就不知道要给谁设置值了
<style> #box { width: 200px; height: 200px; background-color: red; padding: 20px; margin: 1.875rem; border: 20px solid #00F7DE; } </style> <button>点击</button> <div id="box">sd</div> <script> document.getElementsByTagName("button")[0].onclick = function() { var box = document.getElementById("box"); console.log(box.clientHeight) } </script>
2、元素可见区宽高2
element.offsetHeight | 返回任何一个元素的高度,包括边框和填充,但不是边距 |
---|---|
element.offsetWidth | 返回元素的宽度,包括边框和填充,但不是边距 |
这两个属性和元素可见区宽高1中的两个属性类似,只不过是多相加了一个样式(边框)。
属性也是只读。
3、获取到最近的定位祖先元素
element.offsetParent
可以用来获取当前元素的定位祖先元素。
会获取到离当前元素最近的开启了定位的祖先元素,如果所有的祖先元素都没有开启定位,则返回body对象。
<button>点击</button>
<div id="b1" style="position: relative;">
<div id="box">sd</div>
</div>
<div id="box2">sd</div>
<script>
document.getElementsByTagName("button")[0].onclick = function() {
var box = document.getElementById("box");
console.log(box.offsetParent); //<div...
var box2 = document.getElementById("box2");
console.log(box2.offsetParent); //<body>
}
</script>
4、元素相对偏移位置
element.offsetLeft | 返回当前元素的相对水平偏移位置的偏移容器 |
---|---|
element.offsetTop | 返回当前元素的相对垂直偏移位置的偏移容器 |
element.offsetLeft:— 当前元素相对于其定位祖先元素的水平偏移量
element.offsetTop:— 当前元素相对于其定位祖先元素的垂直偏移量
<style> *{margin: 0;padding: 0;} .box { width: 50px; height: 50px; background-color: red; padding: 20px; border: 20px solid #00F7DE; } #b1{padding: 30px;} </style> <button>点击</button> <div id="b1" style="position: relative;"> <div id="box" class="box">sd</div> </div> <div id="box2" class="box">sd</div> <script> document.getElementsByTagName("button")[0].onclick = function() { var box = document.getElementById("box"); console.log(box.offsetLeft); var box2 = document.getElementById("box2"); console.log(box2.offsetLeft); } </script>
5、滚动条相关
element.scrollHeight | 返回整个元素的高度(包括带滚动条的隐蔽的地方) |
---|---|
element.scrollWidth | 返回元素的整个宽度(包括带滚动条的隐蔽的地方) |
element.scrollLeft | 返回当前视图中的实际元素的左边缘和左边缘之间的距离。获取滚动条滚动的距离 |
element.scrollTop | 返回当前视图中的实际元素的顶部边缘和顶部边缘之间的距离。获取滚动条滚动的距离 |
<style> *{margin: 0;padding: 0;} .b1{ width: 100px; height: 150px; background-color: red; overflow: auto; margin: 20px; } .box { width: 200px; height: 500px; background-color: yellow; margin: 20px; } </style> <button>点击</button> <div id="b1" class="b1" style="position: relative;"> <div id="box" class="box">sd</div> </div> <script> document.getElementsByTagName("button")[0].onclick = function() { var box = document.getElementById("box"); console.log(box.scrollHeight) // 500 console.log(box.scrollWidth) // 200 console.log(box.offsetWidth) // 200 console.log(box.clientWidth) // 200 console.log(box.scrollLeft) // 0 console.log(box.scrollTop) // 0 console.log("-------") var b1 = document.getElementById("b1"); console.log(b1.scrollHeight) // 540 console.log(b1.scrollWidth) // 220 console.log(b1.offsetWidth) // 100 console.log(b1.clientWidth) // 83 console.log(b1.clientHeight) // 133 console.log(b1.scrollLeft) // 117 console.log(b1.scrollTop) // 407 } </script>
注意啊:滚动条会占有17px的像素。
小应用:判断滚动是否到达底部:
scrollWidth - scrollLeft == clientWidth
1、浏览器滚动条
chrome认为浏览器的滚动条是body的,可以通过 body.scrollTop 来获取。
火狐等浏览器认为浏览器的滚动条是html的,可以通过 decument.decomentElement.scrollTop 来获取到。
所以这里就有兼容的问题了,兼容如下:
var st = document.body.scrollTop || document.documentElement.scrollTop var sl = document.body.scrollLeft || document.documentElement.scrollLeft
事件
- 事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间
- js与html之间的交互式通过事件实现的
- 对于web应用来说,有下面这些代表性的事件:点击某个元素、将鼠标移动至某个元素上、按下键盘上某个键,等等
1、事件的进击之路
一个简单的例子:
<body>
<button id="but" onclick="alert('点击了')">按钮</button>
</body>
但是这样的写法使得js代码和html文档的耦合度太高了,维护的代价太大,所以不推荐使用这种方法。
下面介绍另一种方法:让事件调用js的函数
<body>
<button id="but" onclick="fun()">按钮</button>
</body>
<script>
function fun(){
alert("点击了")
}
</script>
但是这种方式还是有一些不好的地方的,但具体的不好再哪里我也不知道,因为js规范中有提供了一种事件绑定的方式,说明这种方式还不是最终好的
最后介绍一种事件的绑定方式,称之为事件绑定:
<body>
<button id="but" onclick="fun()">按钮</button>
</body>
<script>
var but = document.getElementById("but");
but.onclick = function (){
alert("点击了")
}
</script>
强烈推荐使用这种方式。
2、事件都有哪些呢
属性 | 当以下情况发生时,出现此事件 |
---|---|
onabort | 图像加载被中断 |
onblur | 元素失去焦点 |
onchange | 用户改变域的内容 |
onclick | 鼠标点击某个对象 |
ondblclick | 鼠标双击某个对象 |
onerror | 当加载文档或图像时发生某个错误 |
onfocus | 元素获得焦点 |
onkeydown | 某个键盘的键被按下 |
onkeypress | 某个键盘的键被按下或按住 |
onkeyup | 某个键盘的键被松开 |
onload | 某个页面或图像被完成加载 |
onmousedown | 某个鼠标按键被按下 |
onmousemove | 鼠标被移动 |
onmouseout | 鼠标从某元素移开 |
onmouseover | 鼠标被移到某元素之上 |
onmouseup | 某个鼠标按键被松开 |
onreset | 重置按钮被点击 |
onresize | 窗口或框架被调整尺寸 |
onselect | 文本被选定 |
onsubmit | 提交按钮被点击 |
onunload | 用户退出页面 |
等等。。。。还有很多的事件,就不一一都列出来了
3、事件绑定函数中的this
结论:在事件的相应函数中,相应函数时给谁绑定的,this就是谁
<input type="checkbox" id="allCheckbox"/> 全选
<script>
var allCheckbox = document.getElementById("allCheckbox");
allCheckbox.onclick = function(){
console.log(this) // <input type="checkbox" id="allCheckbox"/> 全选
alert(this == allCheckbox); // true
}
</script>
事件对象 –
1、传参
当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为参数传递进响应函数。
在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标、键盘那个按键被按下、鼠标滚轮滚动…
<style>
* {margin: 0;padding: 0;}
.b1 {width: 300px;height: 100px;
border: 2px solid #DDDDDD;margin: 20px;}
.box {width: 300px;height: 30px;
border: 2px solid #DDDDDD;margin: 20px;}
</style>
<div id="b1" class="b1" style="position: relative;">
</div>
<div id="box" class="box"></div>
<script>
// 第1种方法
document.getElementById("b1").onmousemove = function(event){
console.log(event);
}
// 第二种方法
document.getElementById("box").onmousemove = function(){
console.log(arguments[0]);
}
</script>
1、兼容
在IE8中,响应函数被处理时,浏览器不会传递事件对象给函数,但是在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的,所以,可以通过window对象获取到事件对象。
document.getElementById("b1").onmousemove = function(event){
event = event || window.event
console.log(event);
}
2、冒泡(bubble)
冒泡:所谓的冒泡指的是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发。
取消冒泡:语法:
event.cancelBubble = true;
<style>
.box {width: 200px;height: 100px;border: 2px solid #DDDDDD;background-color: #93D1FF;}
#s1 {background-color: greenyellow;}
</style>
<div id="box" class="box">
我是box
<span id="s1">
我是span
</span>
</div>
<script>
window.onload = function() {
var box = document.getElementById("box");
var s1 = document.getElementById("s1");
box.onclick = function() {
alert("this is span")
}
s1.onclick = function(event) {
alert("this is box")
// 取消冒泡,例如取消span元素的冒泡:
// event.cancelBubble = true;
}
document.body.onclick = function() {
alert("this is body")
}
// 通过上面的测试,可以知道:
// 当点击body时,只有body事件被触发了;
// 当点击box元素时,有box元素和包含box元素的祖先元素body被触发了;
// 当点击span元素时,包含span元素的祖先元素和span元素都被触发了;
}
</script>
3、委派
事件的委派:
- 指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素。
- 事件委派是利用了冒泡的特性
<style>
.box {width: 200px;height: 100px;border: 2px solid #DDDDDD;background-color: #93D1FF;}
#s1 {background-color: greenyellow;}
</style>
<button>click</button>
<ul id="ul">
<li><a href="javascript:;" class="link">a1</a></li>
<li><a href="javascript:;" class="link">a2</a></li>
<li><a href="javascript:;" class="link">a3</a></li>
</ul>
<script>
window.onload = function() {
var ul = document.getElementsByTagName("ul")[0]
var a = document.getElementsByTagName("a");
var button = document.getElementsByTagName("button")[0];
// 点击按钮后添加链接
button.onclick = function(event) {
var li = document.createElement("li");
li.innerHTML = '<li><a href="javascript:;" class="link">这是新建的链接</a></li>'
ul.appendChild(li);
}
/*
作用:为每一个链接绑定一个单击响应函数
这里我们为每一个超链接都绑定了一个单击响应函数,但是这种方法比较麻烦,
而且这些操作只能为已经存在的超链接设置事件,而新添加的超链接必须重新绑定。
所以,我们希望即使是后面新添加的超链接也能绑定到我们定义好的响应函数。
*/
// for (let i = 0; i < a.length; i++) {
// a[i].onclick = function() {
// alert("this is a" + (i + 1) + " ~~~");
// }
// }
/*
综合以上,我们需要有一种方式,能只绑定一次事件,能应用到多个的元素上,即使元素时后续添加的。
解决方法:
我们可以将其响应函数绑定给元素的共同的祖先元素
*/
ul.onclick = function(event) {
var event = event || window.event;
if (event.target.className == "link") {
console.log("this is a")
}
}
}
</script>
4、事件的绑定
先看例子:
<button>click</button>
<script>
window.onload = function() {
var button = document.getElementsByTagName("button")[0];
button.onclick = function(event) {
alert(1)
}
button.onclick = function(event) {
alert(2)
}
}
</script>
实验完上面的例子后会发现,使用 对象.事件 = 函数
的形式为对象绑定响应函数,只能同时为一个元素的同一事件绑定一个响应函数,不能绑定多个,如果绑定了多个,则后边的函数会覆盖前面的函数。
若同时为一个元素的相同事件绑定多个响应函数,可以使用注册监听事件方法:
<button>click</button>
<script>
var button = document.getElementsByTagName("button")[0];
button.addEventListener("click", function() {
alert("3") ;
},false)
// 好主意这个方法不支持IE8,IE8要使用attachEvent()方法,查看下面的-注册监听事件
button.addEventListener("click", function() {
alert("4");
},false)
button.onclick = function(event) {
alert(5)
}
</script>
5、事件的传播
事件的传播(了解即可)
- 关于事件的传播,网景公司和微软公司有不同的理解
- 微软公司:认为事件应该是有内向外传播,也就是当事假触发时,应该先触发当前元素上的事件,然后再向当前元素的足下元素上传播,也就是说事件应该在冒泡阶段执行。
- 网景公司:认为事件应该是有外向内传播的,也就是当前事件触发时,应该先触发当前元素的最外层的祖先元素的事件,然后再向内传播给后代元素。
但是事件的传播最后究竟是由谁来决定?答案:w3c:
w3c综合了两个公司的方案,将事件传播分成了三个阶段:
- 捕获阶段:
- 在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
- 目标阶段
- 事件捕获到目标元素,捕获结束开始在目标元素触发事件
- 冒泡阶段
- 事件从目标元素向他的祖先元素传递,一次触发祖先元素上的事件。
注意:
- 如果希望在捕获阶段就触发事件,可以将addEventListener() 方法的第三个参数设置为true,一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false
- IE9及以下的浏览器中没有捕获阶段。
用于判断事件的传播是在哪个阶段,可以用事件对象的常量了进行判断:
CAPTURING-PHASE | 当前事件阶段为捕获阶段(1) |
---|---|
AT-TARGET | 当前事件是目标阶段,在评估目标事件(1) |
BUBBLING-PHASE | 当前的事件为冒泡阶段 (3) |
6、常用属性
1、获取目标对象
语法:event.target
event中的target表示的是触发事件的对象;返回触发此事件的元素(事件的目标节点)。即使是通过冒泡触发的,返回的也是最先的那个元素。
<ul id="ul"> <p>this si p</p> <li><a href="javascript:;">a1</a></li> <li><a href="javascript:;">a2</a></li> </ul> <script> var ul = document.getElementsByTagName("ul")[0] ul.onclick = function(event){ var event = event || window.event; alert(event.target) } </script>
7、常用方法
1、取消默认行为
preventDefault() | 通知浏览器不要执行与事件关联的默认动作。 |
---|---|
用法为:event.preventDefault()
目标事件对象
1、注册监听事件
a、element.addEventListener()
语法:element.addEventListener()
通过这个方法也可以为元素绑定响应函数,并且可以为同一个元素绑定多个相同事件。这样当事件被触发时,响应函数将会按照响应函数的绑定顺序执行。
参数:
- 事件的字符串,但是不要前面的 “on” ,直接写后面的即可
- 会调函数,当事件触发时该函数会被调用
- 是否在捕获阶段触发事件,需要一个布尔值,一般都是传false
<button>click</button> <script> var button = document.getElementsByTagName("button")[0]; button.addEventListener("click", function() { alert("3") ; },false) button.addEventListener("click", function() { alert("4"); },false) button.onclick = function(event) { alert(5) } </script>
注意:
- 该方法中的 this 指的是绑定事件的对象
- 这个方法在IE8及以下的浏览器不支持。
b、element.attachEvent()
语法:element.attachEvent()
在IE8及以下的浏览器不支持element.addEventListener()方法,所以要使用该**element.attachEvent()**方法。
这个方法也可以为同一个元素绑定多个相同的事件,和addEventListener()方法不同的是后绑定先执行,执行顺序和addEventListener()方法相反。
参数:
- 事件字符串,和addEventListener()不同,这个方法要 “on”
- 会调函数
<button>click</button> <script> var button = document.getElementsByTagName("button")[0]; button.attachEvent("click", function() { alert("3") ; }) button.attachEvent("click", function() { alert("4"); }) button.onclick = function(event) { alert(5) } </script>
主意:
- 该方法中的 this 是window
c、兼容
定义一个函数,用来为指定的元素绑定响应函数
要求,因为两个绑定事件的方法的this不同,所以要统一两个方法的this。
/* * obj -要绑定事件的对象 * eventStr -事件的字符串(不要 on) * callBack -回调函数 */ function bind(obj, eventStr, callBack) { if (obj.addEventListener) { obj.addEventListener(eventStr, callBack, false) } else { obj.attachEvent("on" + eventStr, function(){ // 在匿名函数中调用回调函数,这样就可以自定this是谁了。-_-!! callBack.call(obj); }) } }
一个例子:
<button>click</button> <script> var button = document.getElementsByTagName("button")[0]; bind(button, "click", function() { alert("----") }) </script>
鼠标/键盘事件对象
1、获取鼠标坐标
a、浏览器窗口的坐标
clientX | 返回当事件被触发时,鼠标指针的水平坐标。(相对浏览器窗口) |
---|---|
clientY | 返回当事件被触发时,鼠标指针的垂直坐标。(相对浏览器窗口) |
注意:上面的两个的两个属性是获取鼠标在当前的可见窗口的坐标(相当于是浏览器窗口)
用个图来说明一下 :
其中,红色的是浏览器的可见窗口,蓝色的是文档的窗口,而上面的图表示网页文档的窗口往上移动了。而我们的clientX属性和clientY属性就是获取 浏览器的可见窗口的坐标,既是空色部分的左上角永远都是(0, 0)。
语法:event.clientX 与 event.clientY
document.onmousemove = function(event) { var event = event || window.event; // 获取到鼠标的坐标 var top = event.clientY; var left = event.clientX; console.log(event.clientX, "---", event.clientY) }
b、文档页面窗口的坐标
pageX | 返回当事件被触发时,鼠标指针的水平坐标。(相对当前页面文档窗口) |
---|---|
pageY | 返回当事件被触发时,鼠标指针的垂直坐标。(相对当前页面文档窗口) |
pageX和pageY可以获取鼠标相对于当前页面的坐标
注意:但是这两个属性在IE8中不支持,所以如果需要兼容IE8,则不要使用者两个属性。
语法:语法:event.pageX 与 event.pageY
c、一个例子:div元素跟随鼠标移动
<style>
.box {width: 100px;height: 100px;border: 2px solid #DDDDDD;
/* margin: 20px; */
background-color: #93D1FF;
/* 移动要开启元素的定位属性 */
position: absolute;
}
</style>
<div id="box" class="box"></div>
<script>
var box = document.getElementById("box");
document.onmousemove = function(event) {
var event = event || window.event;
var st = document.body.scrollTop || document.documentElement.scrollTop
var sl = document.body.scrollLeft || document.documentElement.scrollLeft
// 获取到鼠标的坐标
var top = event.clientY;
var left = event.clientX;
console.log(event.clientX, "---", event.clientY)
box.style.top = (top + st) + "px";
box.style.left = (left+sl) + "px";
}
</script>
2、滚轮的事件
onmousewheel鼠标滚轮滚动的事件,会在滚轮滚动时触发,但是火狐不支持该属性。
在火狐中需要使用 DOMMouseScroll 来绑定滚动事件,而且要注意,该事件需要通过addEventListener()函数来绑定
<style>
#box{
width: 6.25rem;
height: 6.25rem;
background-color: palevioletred;
}
</style>
<div id="box"></div>
<script>
var box = document.getElementById("box");
box.onwheel = function(){
var event = event || window.event;
console.log(event.detail);
if (event.wheelDelta > 0 || event.detail < 0) {
box.style.height = (box.clientHeight - 10) + "px";
} else {
box.style.height = (box.clientHeight + 10) + "px";
}
// 使用addEventListener() 方法绑定响应函数的,
// 取消默认行为时不能使用 return false
// 需要使用时间event 来取消默认行为
// 但是IE8不支持该数次那个。或报错。
event.preventDefault || event.preventDefault();
// 取消默認行為
return false;
}
addEventListener(box, "DOMMouseScroll", box.onwheel)
</script>
判断滚轮滚动的方向:判断鼠标滚动(滚动条)方向(wheelDelta和detail)
wheelDelta和detail是判断鼠标滚动方向的指标,区别是:
wheelDelta的值为正(120,240…)则是鼠标向上;为负(-120,-240)则是向下。
detail则是相反的,数值不一样。
它俩针对的是不同浏览器,具体啥浏览器。
// event.wheelDelta 可以获取鼠标滚轮滚动的方向 // 向上滚为 120,向下滚为 -120 // console.log(event.wheelDelta); // 但是火狐中不支持event.wheelDelta属性 // 在火狐中使用event.detail 属性来获取滚轮滚动方向 // 向上滚为 -3, 向下滚为 3 // console.log(event.detail);
3、键盘事件
主要有三个方法:
onkeydown 某个键盘按键被按下。 onkeypress 某个键盘按键被按下并松开。 onkeyup 某个键盘按键被松开。 键盘事件一般都会绑定给一些可以获取到焦点的对象或是document。
onkeydown:
如果一直按着某个按键不松手,则时间一直触发。
<input type="text" placeholder="click" id="text"/> <script> // 键盘事件一般都会绑定给一些可以获取到焦点的对象或是document。 var text = document.getElementById("text"); text.onkeydown = function(event){ var event = event || window.event console.log(event.keyCode) console.log(event.key) console.log(event.shiftKey) console.log(event.ctrlKey) console.log(event.altKey) console.log("-----===------") // 在文本框中输入内容,属于onkeydown的默认行为, // 如果在onkeydown中取消了默认行为,则输入的内容不会出现在文本框中。 // return false; // 不能输入数字 if(event.keyCode >= 48 && event.keyCode <= 57){ console.log("-----===1------") return false; } } document.onkeydown = function(){} </script>
----------
取消默认行为
<a>标签
a、在js函数中返回false
<a id="a" href="add?id=1"> 全选</a> <script> var a = document.getElementById() a.onclick = function(){ alert("..") reture false; } </script>
b、在href中写入JavaScript:;
<a id="a" href="JavaScript:;"> 全选</a>
c、使用事件中提供的方法:
event.preventDefault || event.preventDefault();
注意:
// 使用addEventListener() 方法绑定响应函数的,
// 取消默认行为时不能使用 return false
// 需要使用时间event 来取消默认行为
// 但是IE8不支持该数次那个。或报错。
强盗方法setCapture()
<script type="text/javascript">
window.onload = function(){
//为两个按钮绑定单击响应函数
var btn01 = document.getElementById("btn01");
var btn02 = document.getElementById("btn02");
btn01.onclick = function(){
alert(1);
}
btn02.onclick = function(){
alert(2);
}
/* 设当btne1对标按下相关的事件进行捕获,
* 当调用一个元素的setCapture()方法以后,
* 这个元将会把下一次所有的鼠标按下相关的事件到自身上;
* 元素将会自动将下一次鼠标点击相关的事件捕获到自身上
*/
btn01.setCapture();
};
</script>
<body>
<button id="btn01">按钮1</button>
<button id="btn02">按钮2</button>
</body>
注意:
- 这个方法好像现在不能用了,测试了一下,不起作用了。
- 这个方法只有IE支持,但是在火狐中调用时不会报错,而在Chrome中会报错