DOM 文档对象模型 document Object Model
面试题:HTML/XHTML/DHTML/XML分别是什么?
1、HTML - 网页
2、XHTML - 更严格的网页
3、DHTML - 动态的网页:D:Dynamic:其实并不是新技术、新概念,是将现有技术的整合的统称,使我们的网页在离线状态依然具有动态效果
DHTML:HTML+CSS+JS(dom);
4、XML - 数据格式
DOM树:把HTML看做了是一个倒挂的树状结构,但是树根并不是HTML,而是document对象(属性和方法)
document对象不需要程序员创建,由浏览器的js解释器自动创建,一个页面只有一个document
注意:DOM会把页面上所有的元素都看做是一个DOM对象、DOM节点、DOM元素 - 都是同一个意思
DOM原本是可以操作一切结构化文档的HTML和XML,后面为了方便各类的开发者分为了三部分:
核心DOM:既可以操作HTML又可以操作XML——缺点:比较繁琐
HTMLDOM:只能操作HTML,API简单——缺点:如属性部分,只能访问/设置标准属性,不能设置/获取自定义属性;
XMLDOM:只能操作XML,淘汰
(一)、每个DOM元素都有三大属性
1、xx.nodeType:描述节点的类型
document节点:9
element节点:1
attribute节点:2
text节点:3
以前有用,判断xx是不是一个页面元素 - 因为以前我们找元素的方法和大家现在不一样
2、xx.nodeValue:属性节点的值,说白了获取属性值
以前有用,获取一个属性节点的值
3、***xx.nodeName:节点的名称 - 判断xx是什么标签
注意:返回是一个全大写的标签名
案例:
<div id="div">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nemo hic voluptatem laborum corporis iure magni doloremque sint adipisci voluptatum dolorum voluptatibus fugit mollitia eligendi provident minus architecto cumque perferendis repellat?</p>
<span>aaaa</span>
<b>lalala</b>
</div>
<script type="text/javascript">
var arr=div.children;
for(var i=0;i<arr.length;i++){
if(arr[i].nodeName=="P"){
arr[i].style.background="red";
}else if(arr[i].nodeName=="SPAN"){
arr[i].style.background="orange";
}else if(arr[i].nodeName=="B"){
arr[i].style.background="yellow";
}else if(arr[i].nodeName=="I"){
arr[i].style.background="green";
}else if(arr[i].nodeName=="DIV"){
arr[i].style.background="cyan";
}else if(arr[i].nodeName=="H1"){
arr[i].style.background="blue";
}else if(arr[i].nodeName=="H2"){
arr[i].style.background="purple";
}
}
div.onclick=e=>{
if(e.target.nodeName=="P" || e.target.nodeName=="SPAN" || e.target.nodeName=="B"){
e.target.style.background="red";
}
}
(二)、找元素
【1】、直接找元素
【1.1】通过 HTML 的属性去获取元素
通过ID:
语法:var elem=document.getElementById("id值");
返回:是一个DOM对象/DOM节点/DOM元素,才可以直接做操作
特殊:
1、如果id重复了,只会返回第一个
*****2、如果没找到,返回的是一个null
*通过 标签名:
语法:var elems=document/parent.getElementsByTagName("标签名");
返回:是一个DOM集合 - 是一个类数组对象:类似数组,也可以使用下标、length、遍历
特殊:
1、一个DOM集合是不允许直接做操作的,除非加下标得到某一个 或 遍历得到所有人,才能做操作
2、如果没找到,返回的是一个空集合
3、此方法不一定非要写document,也可以写一个parent(已经找到的某个父元素)
如果是写的document,则会找到页面上所有的标签
而写的是parent,只会找到parent下面的所有的标签
*通过 class名:
语法:var elems=document/parent.getElementsByClassName("class名");
返回:是一个DOM集合 - 是一个类数组对象:类似数组,也可以使用下标、length、遍历
特殊:
1、一个DOM集合是不允许直接做操作的,除非加下标得到某一个 或 遍历得到所有人,才能做操作
2、如果没找到,返回的是一个空集合
3、此方法不一定非要写document,也可以写一个parent(已经找到的某个父元素)
如果是写的document,则会找到页面上所有的标签
而写的是parent,只会找到parent下面的所有的标签
通过 Name:
语法:var elems=document/parent.getElementsByName("属性名");
找到了是一个集合,没找到是一个空集合
建议:表单控件元素尽量可以不用写class,因为必写name
【1.2】、*通过css选择器去获取元素
1、单个元素:var elem=document.querySelector("任意css选择器");
强调:万一选择器匹配到多个,只会返回第一个
没找到null
2、多个元素:var elems=document.querySelectorAll("任意css选择器");
强调:找到了返回集合,没找到返回空集合
更适合做复杂查找
面试题:getXXX 和 querySelectoXXX 有什么区别?
返回结果不同:
1、getXXX,获取到返回的是一个动态集合HTMLCollection
2、querySelectoXXX,获取到返回的是一个静态集合NodeList
动态集合 vs 静态集合
1、动态集合:根据DOM树的改变,是动态集合也一起改变,每一次修改DOM树,都会悄悄的再次查找DOM
缺点:每次都会悄悄重新查找,效率较低,不能使用forEach
2、静态集合:每一次修改DOM树,静态集合不会发生变化,只会认准你找的时候的第一次找到的结果
优点:每次不会悄悄重新查找,效率较高,可以使用forEach
【2】、*通过 节点之间的关系 获取元素
前提:至少先要找到一个元素,才能找关系
1、父元素:xx.parentNode - 单个元素
2、子元素:xx.children - 集合
3、第一个儿子:xx.firstElementChild - 单个元素
4、最后一个儿子:xx.lastElementChild - 单个元素
5、前一个兄弟:xx.previousElementSibling - 单个元素
6、后一个兄弟:xx.nextElementSibling - 单个元素
【3】查找层级不明确的DPM树结构
递归
简单来说就是函数中,又一次调用了函数自己,迟早有一天会停下来
何时使用:遍历DOM树,专门用于【遍历层级不明确】的情况,既可以遍历层级不明确的DOM,也可以遍历层级不明确的数据
如何使用递归:2步
function 函数名(root){
1、第一层你要做什么操作直接做
2、判断他有没有下一级,如果由下一级再次调用此函数,只不过传入的实参是他的下一次
}
算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕才会跳到兄弟节点
缺点:同时开启大量的函数调用,浪费的内存,只有一个情况采用:【遍历层级不明确】
递归 vs 循环
递归:优点:直观,易用
缺点:性能较低
循环:优点:性能高,几乎不占用内存
遍历层级不明确的API: Tree.Walker
缺陷:专门为遍历层级不明确的DOM准备
如何:
固定公式:
1、创建tw:var tw=document.creatTreeWalker(根元素,NodeFilter.SHOW_ALL/ELEMENT);
2、反复调用nextNode方法找到每一个元素
while((node=tw.nextNode())!=null){
node要做什么操作;
}
纯循环
(二)、操作元素
<elem 属性名="属性值" style="样式">内容
【1】、内容:
1、*innerHTML:用于操作双标签,能够识别标签,没有兼容性问题
获取:elem.innerHTML;
设置/修改:elem.innerHTML="新内容";
2、elem.textContent:获取或设置开始标签到结束标签之间的纯文本,用于操作双标签,不能识别标签,有兼容性问题
获取:elem.textContent;
设置/修改:elem.textContent="新内容";
*老IE:elem.innerText;//第一次碰到小三上位,原本是只有老IE才可以使用的,但是随着各个浏览器的发展,IE没有将就大家,反而是其他主流浏览器去将就了老IE
3、*value:用于操作input
获取:elem.value;
设置/修改:elem.value="新内容";
【2】、*属性:
1、获取属性:
核心DOM:elem.getAttribute("属性名");
HTMLDOM:elem.属性名;
2、设置/修改属性
核心DOM:elem.setAttribute("属性名","属性值");
HTMLDOM:elem.属性名="属性值";
3、删除属性:
核心DOM:elem.removeAttribute("属性名");
HTML DOM:elem.属性名="";
4、判断属性:垃圾,只能判断有没有,不能判断具体是什么
核心DOM:elem.hasAttribute("属性名");
真的想要判断,更推荐:
if(elem.getAttribute("class")=="d1"){
使用获取的方式能判断出具体是什么class
}
强调:HTML DOM确实简单,但是需要注意:
1、class写为className
2、只能操作标准属性,不能操作自定义属性
3、删除的时候,删不干净,有的属性删不干净依然具有功能,比如href没有属性值默认为刷新、disabled就算没有属性值依然是禁用操作...,跟推荐删除的时候使用核心DOM
【3】、样式:
内联样式:
优点:1、优先级最高 - 保证js用户触发一定会生效
2、不会牵一发动全身 - 只会操作当前元素
缺点:1、获取样式时,一定要保证此样式在内联样式中
获取样式:elem.style.css属性名;
设置样式:elem.style.css属性名="css属性值";
特殊:css属性名的写法,如果有横线要去掉横线,变为小驼峰命名法
原来CSS JS:
background-color backgroundColor
width width
border-top-width borderTopWidth
样式表
//1、获取你想要操作的样式表
var sheet=document.styleSheets[1];
//2、获取这个样式表中所有的样式规则
var rules=sheet.cssRules;
//3、数出你想要操作的那个样式规则
var rule=rules[30];
//4、操作:获取和设置
console.log(rule.style.background);
rule.style.background="purple"
【4】、*绑定事件:
页面上绑定事件,不能保证HTML和CSS和JS分离
如: <button onclick="r()">计算圆周长</button>
单个元素:xx.onclick=function(){
this->xx
}
集合:
for(var i=0;i<elems.length;i++){
elems[i].onclick=function(){
this->当前触发的元素
}
}
一切的获取:往往都是用于判断 一切的设置:都是修改
(三)、创建元素和渲染DOM数据:3步
1、创建空标签:
var elem=document.createElement("标签名");
2、为这个空标签添加必要的东西(属性或事件)
elem.属性名="属性值";
elem.on事件名=function(){}
3、将在内存中创建的元素渲染到DOM树上
*父元素.appendChild(elem);//将elem追加到父元素里面当了最后一个儿子
父元素.insertBefore(elem,已有子元素);//将elem追加到父元素里面当了儿子,会插在已有子元素的前面 - 导致所有元素的下标都会改变
父元素.replaceChild(elem,已有子元素);//将elem追加到父元素里面当了儿子,会替换掉已有子元素
2、删除元素:elem.remove();
HTML DOM常用对象:【简化】核心DOM
1、image对象:仅仅只是简化了创建语句
创建:var img=new Image();//完全等效于var img=document.createElement("img");
注意:不是人人都有构造函数创建方式
2、form对象:简化了查找元素
查找form元素:var form=document.forms
查找form元素中的表单控件:var input=form.elements
专属事件:form.onsubmit=function(){//提交事件,只会在提交的一瞬间触发:防止用户输入错误也能提交
return false;//阻止条件
}
*select&option
3、*select对象:
属性:1、*select.options;//得到select下面所有的option,完全等效于xx.children
2、*select.selectedIndex;//获取到当前选中项的下标 - 只要是做联动就必然会用到
方法:1、*select.add(option);//完全等效于appendChild
2、select.remove(i);//删除掉下标i的option
专属事件:select.onchange=function(){//只有选中项发生变化的时候才会触发}
4、*option对象:仅仅只是简化了创建语句
*创建:var opt=new Option("innerHTML","value");
建议:HTML DOM别的无所谓,关键select和option:如果你想要一句话将option创建出来放入到select中:
select.add(new Option("内容","值"))
DOM归纳:作用---增删改查(元素、内容/文本、属性、样式)
### 【1】元素
增:var elem=document.creatElement("标签名");
删:elem.remove()
改:elem.
查:html查找(id、class名、标签名、name名)getElementxxxxx
css选择器 querySelecttorxxxx
通过关系
层级不明确的用**递归、遍历API、纯循环
### 【2】文本/内容
增:elem.innerText/innerHTML/value+="内容"
删:elem.innerText/innerHTML/value=""
改:elem.innerText/innerHTML/value="新内容"
查:elem.innerText/innerHTML/value
### 【3】属性
增:elem.setAttribute("属性名","属性值")
删:elem.removeAttribute("属性名")
改:elem.setAttribute("属性名","新属性值")
查:elem.getAttribute("属性名")
### 【3】样式
增:elem.style.css属性名="css属性值"
删:elem.style.css属性名=""
改:elem.style.css属性名="新的css属性值"
查:elem.style.css属性名
BOM 浏览器对象模型 Browser Object Model
(一)、概念
专门提供了操作浏览器的API--不像DOM有W3C标准,使用得比较少,但是大多数的浏览器厂商已经实现了兼容问题
(二)、对象
window对象
【扮演的角色】
1、浏览器之中,代替了全局对象Global充当了全局作用域(包含了所有的全局对象、变量、函数)
2、指带了当前浏览器的窗口
window对象提供的东西
【网页打开新连接的方式】
主要是为了提升用户的体验感
1、替换当前页面,可以后退:
HTML:<a href="url" target="_self">文字</a>//target="_self"不写的时候默认就是这个
JS:open("url","_self");
2、替换当前页,禁止后退:
history对象:保存了当前窗口的历史记录,功能:前进后退
location:保存了当前窗口正在打开的url
*location.replace("新url");//替换当前网址、不会产生历史记录
使用场景:电商网页:结账完毕后,不允许用户后退
3、在新窗口打开,可以打开多个:
HTML:<a href="url" target="_blank">文字</a>
JS:open("url","_blank");
4、在新窗口打开,只能打开一个:
HTML:<a href="url" target="自定义">文字</a>
JS:open("url","自定义");
自定义:窗口的底层都有一个名字,如果出现重复的名字则新窗口会替换掉原来的旧窗口
使用场景:电商网页:跳转到 支付结账页面,只允许用户打开一个
【扩展:a标签可以做的事?】
1、跳转
2、锚点
3、下载:<a href="xx.rar/zip/exe">文字</a>
4、打开图片、txt:<a href="xx.图片后缀/txt">文字</a>
5、直接书写js:<a href="javascript:js代码;">文字</a>
【window对象的属性】
获取浏览器窗口的大小:
文档显示区域的大小:body部分
innerWidth/innerHeight
完整的浏览器大小:
outerWidth/outerHeight
屏幕的大小:没用 - 桌面应用才用的
screen.width/height
【window对象的方法】
1、打开新窗口:var newW=open("url","自定义name","width=,height=,left=,top=");
特殊:如果没传入第三个参数,新窗口会和浏览器并为一体
如果传入第三个参数,新窗口会脱离浏览器
建议宽高不小于200
有可能会被浏览器拦截
2、关闭窗口:窗口.close();
3、修改窗口的大小:新窗口.resizeTo(newW,newH);
4、修改窗口的位置:新窗口.moveTo(x,y);
【*****定时器:2种】
*1、周期性定时器:每过一段时间,会执行一次定时器中的操作
开启:timer=setInterval(callback,间隔毫秒数)
停止:clearInterval(timer);
2、一次性定时器:等待一段时间,执行一次定时器之中的操作就停止了
开启:timer=setTimeout(callback,等待毫秒数)
停止:clearTimeout(timer);
注意:一次性和周期性底层都是一样的,甚至可以相互转换,所以到底用哪个都可以
总结:函数 和 循环 和 定时器 都能反复执行,区别?
1、函数 - 用户触发绑定事件
2、循环 - 一瞬间基本就结束了
3、定时器 - 等一段时间做一次
hidstory对象
保存当前窗口的历史记录(打开过的URL)可用于
1、前进:hirtory.go(正数)//写几就前进几步
2、后退:hirtory.go(负数)
3、刷新:hirtory.go(0)
location对象***
保存着当前窗口正在打开的url
常识部分:
url的组成部分:【协议://域名/主机号:端口号/文件的相对路径?查询字符串】
如:
https://www.baidu.com/s?wd=%E5%93%88%E5%93%88%E5%93%88%E5%93%88&rsv_spt=1&rsv_iqid=0x92fa18de00229efa&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_dl=tb&rsv_sug3=9&rsv_sug1=1&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&prefixsug=%25E5%2593%2588%25E5%2593%2588%25E5%2593%2588%25E5%2593%2588&rsp=1&inputT=931&rsv_sug4=931
http://127.0.0.1:8020/bom02/new/01-3.html
协议:https/http/ftp/ws(直播);
域名|主机号:域名是需要购买的,在没有购买的时候都是主机号
端口号:默认端口可以省略不写,https默认端口是443,http默认端口80
文件的相对路径:百度把它加密起来了隐藏起来了
查询字符串:get表单提交显示出来的东西
属性和API:
1、属性:获取到一个url的各个部分,不要记忆,直接输出location对象查看
2、功能:跳转:3种
*1、location="url";
2、location.href="url";
3、location.assign("url");
4、跳转后禁止后退:location.replace("新url"); - 不会产生历史纪录
5、刷新:location.reload();
navigator对象(了解即可)
保存浏览器的基本信息 - 通过JS来判断打开的浏览器是什么浏览器并且判断版本号 - 类似css hack(但是css hack只能操作IE浏览器)
属性:navigator.userAgent;//浏览器的相关信息的字符串
真正的功能,判断浏览器是什么对不同的浏览器执行不同的操作,可以说是js版本的css hack,但是用法太过繁琐,发展到现在,很多兼容语法,前辈已经提前提供好了,我们不需要他了
event对象
事件对象:由用户触发的 或 浏览器自动触发
(1)绑定事件
1、在HTML页面上绑定事件:
<elem on事件名="js语句"></elem>
缺点:1、不符合内容(HTML)与行为(JS)的分离
2、无法动态绑定事件(一次只能绑定一个元素)
3、无法同时绑定多个函数对象
*2、在js中使用元素的事件处理函数属性
elem.on事件名=function(){}
优点:1、符合内容(HTML)与行为(JS)的分离
2、动态绑定事件
缺点:无法同时绑定多个函数对象 - 多个函数做成一个函数就不算了缺点
3、使用专门的事件API绑定事件
if(elem.addEventListener){
elem.addEventListener("事件名",callback);
}else{
elem.attachEvent("on事件名",callback);//老IE如果同时绑定了多个函数对象,反而最下面的函数最先执行
}
优点:1、符合内容(HTML)与行为(JS)的分离
2、动态绑定事件
3、同时绑定多个函数对象
*缺点:兼容问题 - 老IE不兼容,麻烦
主流:elem.addEventListener("事件名",callback);
老IE:elem.attachEvent("on事件名",callback);
(2)事件周期
从事件发生,到所有事件处理函数执行完毕的全过程
主流:3个阶段:
1、捕获阶段:记录要发生的事件有哪些
2、目标优先触发:目标元素->当前点击的实际发生事件的元素
3、冒泡触发:向上出发所有记录着的事件
老IE:2个阶段,没有捕获阶段
(3)获取事件对象event
主流:会自动作为事件处理函数的第一个形参传入
elem.on事件名=e=>{e->event};
老TE:event;
兼容:var e=event;//不光老IE可用,主流也可以用 - 小三上位
(4)****获取得到event对象后可做的操作
a、获取鼠标的坐标/位置
1、在事件函数中的小括号里传入一个形参e,自动获取到事件对象event
2、获取坐标:
e.screenX/Y - 鼠标相对于屏幕的坐标:鼠标就算在最上面也是70px左右
e.clientX/Y - 鼠标相对于文档显示区域的坐标:网页不管多长多大,始终顶部是0,底部是900多
*e.pageX/Y - 鼠标相对于整个页面的坐标 - 跟随我们的网页变化
完成鼠标跟随动画:
1、window/document.onmousemove - 在页面的任何位置移动鼠标都会触发
2、js的加载速度比图片的加载速度快,使用加载事件:onload
案例:点不到的div
<body>
<div id="d1">
<button>x</button>
<p>看看你能不能点击到</p>
</div>
<script type="text/javascript">
var rx=parseInt(Math.random()*((innerWidth-200)-0+1)+0);
var ry=parseInt(Math.random()*((innerHeight-200)-0+1)+0);
d1.style.left=rx+"px";
d1.style.top=ry+"px";
d1.onmouseover=function(e){
var x=e.pageX;
var y=e.pageY;
while(true){
var rx=parseInt(Math.random()*((innerWidth-200)-0+1)+0);
var ry=parseInt(Math.random()*((innerHeight-200)-0+1)+0);
if(!(rx<x&&x<rx+200&&ry<y&&y<ry+200)){
d1.style.left=rx+"px";
d1.style.top=ry+"px";
break;
}
}
}
</script>
</body>
b、阻止冒泡(笔试面试之中)
主流:e.stopPropagation();
老IE:e.cancelBubble=true;
** 兼容:if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble=true;
}
c、事件委托(利用冒泡)
开发中常用,为了提升网页性能
优化:如果多个子元素定义了相同 或 相似的操作,最好只能父元素定义一次
为什么:每次绑定一个事件函数,其实都是创建了一个事件对象,创建的事件对象越多,网页效率越低下
淘汰了this:水性杨花,当前元素
【目标元素target】你点击的哪一个,他永远就是那一个,不会变化
主流:e.target;
老IE:e.srcElement;
**兼容:e.srcElement;//不光老IE可用,主流也可以用 - 小三上位
d、阻止浏览器的默认行为
a标签默认会跳转页面,右键自带一个弹出框,F12自带一个开发者工具,F5自带刷新功能,submit按钮默认提交
主流:e.preventDefault();
老IE:e.returnValue=false;
**兼容:if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue=false;
}
f、获取键盘的键码
e.keyCode; - 小游戏
不要记忆:要么输出看你当前点击的按钮是什么码,或者直接百度搜索
新事件:oncontextmenu - 右键事件
onkeydown - 键盘事件
客户端存储技术
cookie:被淘汰
webstorage:HTML5提出的一个新特性:2种
1、存储内容大小一般支持5MB左右(不同浏览器可能不一样)
2、webstorage通过Window.localStorage和Window.sessionStorage属性来实现本地存储机制
3、相关的API:
添加:xxxxStorage.setItem('key','value')或者xxxxStorage.属性名=值;
//该方法接受一个键和值作为参数,会把键值对添加存储中,如果键名存在,则更新其对应的值。
获取:xxxxStorage.getItem('key')或者xxxxStorage.属性名
//该方法接收一个键名作为参数,返回键名对应的值
删除:xxxxStorage.removeItem('key')
清空:xxxxStorage.clear()//清空存储中的所有数据
4、备注
(1)、localStorage - 本地级:永久存在(存储的内容,需要手动清除才会消失,有两种方式:)
(2)、sessionStorage - 会话级:只要浏览器一旦关闭,数据就会死亡(存储的内容会随着浏览器窗口关闭而消失)
(3)xxxxStorage.getItem(xxx):如果xxx对应的value值获取不到,namegetItem的返回值是null
(4)localStorage和sessionStorage存储的内容都是字符出纳,不管是不是字符串都会自动套用toString方法转为字符串,
如果值是对象,可以使用json.stringfy转为字符串。json.parse(null)的结果依然是null
作用:跳转页面后,甚至关闭页面后,数据依然存在
如何使用:不需要创建,浏览器js解释器自动创建,我们直接使用