DOM

Posted sidings

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DOM相关的知识,希望对你有一定的参考价值。

 

1. DOM

   什么是

***DOM Tree

   查找

     按节点间关系查找

     遍历

     查找API

        按html查找(了解)

        按Selector查找

 

1. 什么是: Document Object Model

     专门操作HTML内容的API

   原生js: ES(核心语法)

              +DOM(操作网页内容的API)  3天

              +BOM(操作浏览器窗口的API) 2天

   DOM: W3C 专门操作HTML内容的API标准

 

   DOM分为:

       核心DOM: 能够操作所有结构化文档(HTML, XML)

           万能, 繁琐

       HTML DOM: 专门操作HTML内容的API

           对部分常用API的简化

           简单,  不是万能!

       真正开发: 先用简单的!如果简单的实现不了,再用核心DOM补充

 

2. ***DOM Tree:

   HTML中每个内容(元素,属性,文本)都是一个节点对象(Node)

   所有节点对象都以树形结构存储在一起

   document对象是整棵树的根节点

   所有Node对象都是document的子代节点

   今后,实现任何功能前,必须在头脑中构建出一个树形结构

 

   节点对象: Node 是所有节点对象的父类型

      三大属性:

      nodeType: 节点类型  number

         专门区分节点的类型:

         document: 9

         element:    1

         attribute:   2

         text:           3

         问题: 无法进一步区分元素的标签名

      nodeName: 节点名

         专门区分节点的名称:

         document: #document

         element:    标签名(全大写)

         attribute:   属性名

         text:           #text

      nodeValue: 节点值(了解)

         document: null

         element:    null

         attribute:   属性值

         text:           文本的内容

         

***DOM操作: 都要先找到元素,再修改元素

        四大操作: 查找,修改,添加,删除

 

3. ***查找:

 1. 三个元素不用找,可直接获得:

     <html>  document.documentElement

     <head> document.head

     <body> document.body

 2. 节点间关系:  2大类:

    何时: 只要已经获得了一个节点,找周围节点时

    节点树: 包含所有节点(元素,文本)的完整的树结构

     1. 父子关系: elem.parentNode  返回一个父节点对象

                         elem.childNodes   返回子节点对象的集合

                         elem.firstChild      返回第一个子节点对象

                         elem.lastChild       返回最后一个子节点对象

     2. 兄弟关系:

           elem.previousSibling  : 返回当前节点的前一个兄弟节点

           elem.nextSibling : 返回当前节点的下一个兄弟节点:

    问题: 不但包含有用的元素节点,还包含看不见的换行/空格干扰

    解决: 今后只要仅关心元素节点时,就可用元素树

   元素树: 仅包含元素节点的树结构

     1. 父子关系: elem.parentElement  返回一个父元素对象

                         elem.children       返回子元素对象的集合

            elem.firstElementChild      返回第一个子元素对象

            elem.lastElementChild       返回最后一个子元素对象

     2. 兄弟关系:

            返回当前节点的前一个兄弟元素

            elem.previousElementSibling  :

            返回当前节点的下一个兄弟元素:

            elem.nextElementSibling :

 

***elem.childNodes和elem.children返回的都是动态集合:

     动态集合(live collection): 不实际存储元素和属性值

                                             每次访问集合都重新查找DOM树

     遍历动态结合:

        错误: for(var i=0;i<children.length;i++)

           后果,循环了几次,就重复查找了几次DOM树

        解决: for(var i=0,len=children.length; i<len; i++)

           仅在循环开始时查找一次DOM树,将值另存为在len中

 

  3. 递归遍历: 查找指定父节点下所有子代节点——鄙视题 手写

      如何: 2步:

         1. 先遍历所有直接子节点

         2. 在遍历直接子节点时,对每个子节点调用和父节点完全相同的方法。

      算法: 深度优先遍历: 每次都优先遍历子节点

                                  所有子节点遍历完,才返回遍历兄弟节点

      递归的效率: 极低。

      解决: 绝大多数递归都可用循环代替

 

      遍历API: 每次仅遍历下一个节点,可用循环反复执行(了解)

        1. 节点迭代器:

           如何: 2步:

             1. 创建迭代器对象:

               var iterator=document.createNodeIterator(

           parent,NodeFilter.SHOW_ALL,   null,   false

                                                .SHOW_ELEMENT

               );

             2. var 原先的节点对象=

                      iterator.nextNode(); 跳到下一个节点

                 如果返回null,说明到头了

        内置的就是深度优先遍历算法。

 

   4. 按HTML查找: 在整个页面或指定父元素下,查找属性或标签符合要求得元素对象。

     何时: 只要按基本的条件有选择的查找元素时

     4种:

       1. 按id查找:

           var elem=document.getElementById("id");

           强调: 只能用document调用!

                    仅返回一个元素!

       2. 按标签名查找:

          var elems=parent.getElementsByTagName("标签名");

           强调: 任何父元素都能调用!

                    返回父元素下所有同名标签的元素的集合!

                        即使仅返回一个元素,也会放在集合中

                           如果想取出唯一的一个元素,必须加[0]

                    不仅查找直接子节点,而且查找所有子代节点

       3. 按name属性查找: (了解)

        var elems=document.getElementsByName("name");           4. 按class属性查找: (了解) (兼容性问题)

        var elems=parent.getElementsByClassName("class");

 

   练习:

      关于this:

      在事件处理函数中this->可自动获得事件绑定的元素对象

      何时: 只要处理函数的逻辑中必须当前元素才能正常执行时

      反之: 如果函数的逻辑中不需要当前元素也能正常执行时

               就不用加this!

      DOM操作的固定套路:

        绑定事件->找元素->修改/计算

 

 

 

 

 

 

 

正课:

1. ***查找

   按选择器查找

2. 修改:

 

1. ***查找:

   按选择器查找:

     1. 如果只找一个:

        var elem=parent.querySelector("selector");

     2. 如果找多个:

        var elems=parent.querySelectorAll("selector");

     返回非动态集合(non-live collection):

            实际存储完整数据,反复访问,也不用重新查找DOM树

       优: 不会造成反复查找

 

 鄙视题: getElementsXXXX  vs  querySelector

   1. 返回值:

       getElements返回动态集合:

           优: 首次查找效率高

           缺: 可能造成反复查找DOM树

       querySelector返回非动态集合:

           优: 包含完整属性,无需反复查找DOM树

           缺: 首次查找效率低

    2. 易用性:

       querySelector更灵活, 且更简单

       getElements每次只能查找一种结果,无法设置查找条件

                            更繁琐

    总结: jQuery中:

       如果一次查找就能找到元素时,首选getElements

       如果查找条件复杂时,就用querySelector

 

2. 修改:

   内容:

      1. .innerHTML: 获取或设置元素开始标签到结束标签之间的html代码片段。

      2. .textContent: 获取或设置元素开始标签到结束标签之间的纯文本内容。

           2件事: 1. 去掉了html标签

                      2. 将转义字符转换为正文

          何时: 如果希望去掉内容中的标签,仅获取文本内容时

          .textContent有兼容性问题: IE8不兼容

                 IE8: .innerText

   属性:

   样式:

 

 

 

 

 

 

 

 

正课:

1. 修改:

    属性:

    样式:

 

1. 属性:

    html标准属性:

       核心DOM: 万能

           1. 获取属性:

               1. 获得属性节点对象: (了解)

                var attrNode=elem.attributes[i/属性名];

                                        elem.getAttributeNode("属性名");

                attrNode.value

               2. 直接获得属性值:

                var value=elem.getAttribute("属性名");

           2. 设置属性:

                elem.setAttribute("属性名",新值);

           3. 判断是否包含指定属性:

                var bool=elem.hasAttribute("属性名")

           4. 移除属性:

                elem.removeAttribute("属性名");

           

       HTML DOM: 仅对部分的简化

            elem.属性名

          强调: html中的class属性和ES中的class属性冲突

                 html中的class要改名为className

 

     特例: disabled    selected   checked

        不能用核心DOM操作!只能用html DOM打点操作

        attribute  vs  property

        attribute指出现在开始标签中的属性

        property保存在内存中的对象中的属性

        核心DOM只能操作出现在页面上的attribute属性

           无法操作未出现在页面上的内存中的property属性

        HTML DOM可访问内存中的property属性

 

   自定义属性:  2种:

     1. 只能用核心DOM访问,不能用html访问

     2. HTML5: ——兼容问题

        所有自定义属性: 属性名必须: data-属性名

        访问时: elem.dataset.属性名

   何时: 1. 在客户端网页中临时缓存部分业务数据

            2. 代替id和class作为查找元素的条件

 

2. 样式:

   内联: elem.style.css属性名

     强调: 所有css属性都要去横线变驼峰

        background-color -> backgroundColor

        list-style-type -> listStyleType

   问题1: 只能获得/设置内联样式

           无法获取最终应用到元素上的完整样式

   解决:

      如果设置一个元素的样式: elem.style.css属性名

          因为: 优先级最高!不影响其他元素

      如果获取一个元素的样式: 不用style

          getComputedStyle(elem).css属性名

   问题2: elem.style.css属性名一句话只能设置一个样式

             如果需要同时设置一个元素的多个css属性,代码繁琐

   解决: 今后只要操作一个元素的样式,都用class属性批量操作

     特例: 精确控制动画效果时,需要操作单个css属性

 

   内部/外部样式表:

 

补: .innerHTML只能获得封闭标签的内容

     表单元素的值只能用.value

 

 

 

 

 

 

 

 

 

正课:

1. 添加/删除元素

2. ***HTML DOM常用对象

   select/option    table/行分组/tr/td    form

 

1. 添加/删除元素:

  添加: 3步:

     1. 创建新的空元素:

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

         相当于: <a></a>

     2. 设置元素的关键属性:

         a.href="http://tmooc.cn"

         a.innerHTML="go to tmooc";

         相当于: <a href="http://tmooc.cn">go to tmooc</a>

     3. 将元素添加到DOM树上:

         3种:

         1. 末尾追加: parent.appendChild(a)

         2. 插入在一个现有元素之前:

               parent.insertBefore(a,oldElem)

         3. 替换现有元素: parent.replaceChild(a,oldElem)

 

***页面加载过程:

    html->DOM Tree

                    ↓

                Render Tree->***layout->paint

                    ↑

    css->CSSRules

    结论: 只要修改DOM树的内容,都会导致重新layout

    解决: 优化: 尽量少的操作DOM树

        1. 如果同时添加父元素和子元素

                先再内存中将子元素添加到父元素,再将父元素一次性添加到DOM树

 

 

 

正课:

1. ***HTML DOM常用对象:

   Form

2. BOM

 

1. ***HTML DOM常用对象:

   Form: 代表一个<form元素

   获取: var form=document.forms[i/id/name];

   属性: .length: 相当于form.elements.length

   方法: .submit() : 专用于手动提交表单

          问题: 用户可能按回车,自动提交

          解决: 表单提交的最后一关是一个事件

             form.onsubmit(): 当表单正式提交前自动触发

       获取*表单*元素:

              var elem=form.elements[i/id/name];

              更简化: 如果表单元素有name属性:

                               form.name

       方法: .focus() : 让当前表单元素获得焦点

 

 

 

 

 

 

 

正课:

1. ***BOM

  window

     打开关闭窗口

     窗口大小和位置

     定时器

 

1. ***BOM:

  什么是: Browser Object Model

      专门操作浏览器窗口的API —— 没有标准

  DHTML对象模型:

   window 2个角色:

        1. 代替global作为全局作用域对象

             所有全局函数和全局变量都是window的成员

        2. 封装所有DOM和BOM的API

   包括:

      history 当前窗口打开后成功访问的历史记录

      location 当前窗口正在打开的URL

      document DOM树的根节点,包含所有网页内容

      screen 当前显示设备的信息

      navigator 浏览器的配置信息(版本,名称,插件,cookie)

      event 定义了浏览器中所有事件

 

  window: 指代当前浏览器窗口对象

    打开和关闭窗口

    打开: open("url","name")

    关闭: close();

 

    打开一个连接,共有几种方式:

    1. 在当前窗口打开, 可后退:

       html: <a href="url" target="_self">

       js: open("url","_self");

    2. 在当前窗口打开, 禁止后退:

       js: location.replace("url")

           在当前窗口打开新url,用新url替换history中原有url

       原理:

         history: 保存当前窗口打开后成功访问过的url的历史记录栈

    3. 在新窗口打开,可打开多个:

       html: <a href="url" target="_blank">

       js: open("url","_blank");

    4. 在新窗口打开,只能打开一个:

       html:<a href="url" target="name">

       js: open("url","name");

     每个窗口都有一个唯一的name属性

     name: 在内存中唯一标识一个窗口的名称

     规定: 同时只能打开一个相同name的窗口

       预定义: _self : 和当前窗口使用相同的name

                   _blank: 不指定自定义name,由浏览器随机分配

 

   窗口大小和窗口位置: (了解)

   窗口大小:

      1. 完整窗口大小:  window.outerWidth/outerHeight

 ***2. 文档显示区大小: window.innerWidth/innerHeight

     调整: 2个机会:

      1. 在打开窗口时,就规定大小:

         var config="left=?,top=?,width=?,height=?";

         open("url","name",config)

      2. 打开窗口后再调整——被禁用

       window.resizeTo(width,height)

       window.resizeBy(width增量,height增量)

   窗口位置: 窗口左上角距屏幕左上角的位置

       window.screenLeft|screenX

       window.screenTop|screenY

     调整:

       moveTo(left,top)

       moveBy(left的增量,top的增量)

 

*****定时器:

  什么是: 让程序按照指定时间执行一项任务

  何时:

     1. 让程序每隔一段时间间隔反复执行一项任务——周期性

     2. 让程序等待一段时间后自动执行一次任务——一次性

  如何:

    周期性定时器: 3件事:

     1. 任务函数task

     2. 启动定时器: 将task放入定时器中,设定时间间隔

         timer=setInterval(task, ms);

        timer是唯一标识一个定时器实例的序号

            强调: task不能加(),因为是传递给定时器去回调执行

     3. 停止定时器:

         clearInterval(timer);

        问题: 定时器往往需要自动停止

        解决: 在*任务函数*中判断临界值

                  如果未达到临界值,继续执行任务

                  否则 停止定时器

 

    一次性定时器:  先等待,再执行一次,然后自动释放

    3件事:

     1. 任务函数task

     2. 启动定时器: 将task放入定时器中,设定等待时间

         timer=setTimeout(task, ms);

     3. 停止定时器: (其实是在执行之前,停止等待)

         clearTimeout(timer);

 

   定时器原理:

     定时器中的回调函数,必须在主程序所有语句执行完最后才能开始执行!

 

 

 

 

 

 

 

 

正课:

1. 添加/删除元素

2. ***HTML DOM对象

    select/option table/行分组/tr/td  form

 

1. 添加/删除元素

   优: 尽量少的操作DOM树

   如何:

     1. 如果同时添加父元素和子元素时,都要先将子元素添加到父元素,再将父元素整体添加到DOM树一次即可

     2. 如果同时添加多个平级元素时,都要用文档片段

      文档片段: DocumentFragment

         内存中临时存放多个平级元素的虚拟的临时父元素

         何时: 如果同时添加多个平级元素时,都要先将平级元素添加到文档片段中。再将文档片段一次性添加到DOM树

         如何:

           1. 创建文档片段:

              var frag=document.createDocumentFragment();

           2. 将子元素添加到frag中: frag.appendChild(child)

           3. 将frag添加到DOM树: parent.appendChild(frag)

               frag将子元素添加到DOM树后,自动释放。

 

    删除元素: 1. 先找到要删除的元素对象elem

                   2. parent.removeChild(elem)

        elem.parentNode.removeChild(elem);

 

2. ***HTML DOM常用对象:

    Image   Select/Option    Table/...     Form

  1. Image: 代表一个<img元素

      唯一的简化: var img=new Image();

  2. Select: 代表一个<select元素

      属性:

          .selectedIndex: 获得select中当前选中项的下标

          .options: 获得select中所有的option元素对象

          .options.length: option元素的个数

                                       =0可清除所有option

          其实select.length等效于select.options.length

              最简单的清除所有option的办法: select.length=0;

          .value: 当前select元素选中项的值

               2种情况:

                  1. 如果选中项有value属性,则使用选中项的value属性

                  2. 如果选中项没有value属性,则使用text作为value             

      方法:

         .add(option) => .appendChild

         .remove(i) => select.removeChild(select.options[i])

      事件:

         .onchange: 当选中项发生改变时  

 

      Option: 代表一个<option元素

        创建:

            var opt=new Option(text,value)

        属性:

            .index: 当前option在select中的下标

            .value:

            .text => .innerHTML

            selected:  

   

    其实向select中添加一个option的最简单写法:

       sel.add(new Option(text,value));

 

  3. Table: 代表<table元素

      table

          .createTHead()=>thead

          .deleteTHead()

          .tHead

              .insertRow(i) => tr //在i位置插入一个新行

                                省略i, 默认表示末尾追加

              .deleteRow(i);//删除i位置的行

              .rows //获得thead中的所有行对象

              .rows[i]=>tr

                属性: .rowIndex 获得当前tr相对于整个table的行下标

                                  .insertCell(i) => td //i位置添加一个新td

                                                   省略i, 默认表示末尾追加

                                  .deleteCell(i)

                                  .cells //获得行中的所有格

                                  .cells[i] => td

             

          .createTBody() tbody

          .tBodies/tBody

          

          .createTFoot() tfoot

          .deleteTFoot()

          .tFoot

 

    删除行:

      2种: 1. 用行分组.deleteRow(i)

                     i : 指的是行在当前分组内的下标位置

             2. 用table.deleteRow(i)

                    i: 指的是行在整个table中的下标位置

                           ——tr.rowIndex

       只要用rowIndex删除行,必须用table.deleteRow(...)

 

补: 三类对话框: 都不用

   alert("警告"); 

   var bool=confirm("确认提示");

       点确定->true

       点取消->false

   var input=prompt("输入提示")

         

         

     

 

 

 

正课:

1. *****定时器

   动画

2. screen

3. history

4. ***location

5. ***navigator

6. ****event

 

1. *****定时器

   动画:

  练习:

     获取长度属性值时: 获得都是带单位的字符串,不能直接计算

                                  都要先去单位(parseFloat),再计算

     设置长度属性值时: 在数值结尾拼单位(px)

 

2. screen对象:

   获得屏幕的大小:

       完整大小: screen.width/height

       何时: 用屏幕大小鉴别设备种类: ——鄙视题

          wide desktop  -  lg : 1200+

          pc                    - md: 992+

          pad                  - sm: 768+

          phone              - xs: 480+

   

       可用大小: screen.availWidth/availHeight

           去掉任务栏之后的剩余大小

 

3. history: 保存当前窗口打开后成功访问过的url的历史记录栈

     前进: history.go(1)

     后退: history.go(-1)

     刷新: history.go(0)

 

     其实: history.go(n)

 

4. ***location: 封装当前窗口正在打开的url对象

    属性:

      .href: 获取或设置当前网页打开的url

          何时: 在当前页面打开新链接时

             简写: location.href=url  ->  location=url

      .protocol: 协议

      .host: 主机名+端口号

      .hostname: 主机名

      .port: 端口号

      .pathname: 网页的相对路径

      .hash: #锚点

      .search: ?查询字符串

        鄙视: .search -> 参数组成的对象:

            ?uname=zhangdong&upwd=123456

            ["uname=zhangdong", "upwd=123456"]

            {uname:zhangdong, upwd:123456}

 

 

 

 

 

 

 

 

 

 

正课:

1. ***location

2. ***navigator

3. ****event

 

1. ***location:

   属性:

   方法:

    location.assign("url") => location.href="url"

                                              location="url"

    location.reload(false/true): 重新从服务器加载url

                                 force

         默认false: 优先从服务器端缓存中获取文件

         如果改成true, 强制从服务器硬盘获取新文件

    location.replace(url): 在当前窗口打开新url,禁止后退

 

2. ***navigator: 封装浏览器配置信息的对象

    navigator.cookieEnabled: 是否启用cookie

      cookie: 在客户端本地持久存储一个数据的文件

          如果禁用无法保存搜索关键词,或记住密码

 

    navigator.plugins: 封装所有插件对象的集合

     

    判断浏览器的名称和版本:

    navigator.userAgent: 保存浏览器内核,名称,版本号的字符串

 

3. ****event

   事件: 浏览器自己触发的或用户手动触发的页面(元素)状态的改变。

   浏览器在事件发生时,都会自动调用事件处理函数

   事件处理函数: 当节点发生事件时,自动调用的函数

                          其实就是节点的onxxx属性

      事件处理函数的值,都是一个函数对象

      事件发生时: 节点对象.onxxx();

         事件处理函数中的this: .前的节点对象

 

   何时: 只要希望一个元素/节点,能够响应某个事件时

               在发生某个事件时,能自动执行事件处理函数

   如何: 3种方式:

      1. 在html元素开始标签中:

        html: <ANY on事件名="js语句(this)"

        js: function 函数(ANY){ ... }

        问题: 1. 无法为动态生成的元素绑定事件

                 2. 事件绑定分散在页面各个角落,不便于维护

      2. 在js中绑定事件: 2种方式:

        1. ANY.on事件名=function(){

                 this->ANY

            }

            事件发生时,自动执行: ANY.onXXX();

           问题: 每个ANY的一个事件处理函数,只能绑定一个函数对象。

           解决:

        2. ANY.addEventListener("事件名",fn)

              ANY.removeEventListener("事件名",fn)

           如果只是添加事件监听,可用匿名函数

           如果可能移除某个处理函数,就必须用有名的函数绑定

           IE8: ANY.attachEvent("on事件名",fn)

 

           其实: addEventListener("事件名",fn,capture)

               capture: 是否在捕获阶段就提前触发    

                   默认false: 所有事件处理函数都在冒泡阶段反向触发

                   true: 该事件会在捕获阶段就提前触发!

 

***事件周期:

   DOM标准: 3阶段

      1. 捕获(capture): 由外向内依次记录各级元素绑定的相同事件处理函数

      2. 目标出发: 执行实际触发事件的元素上的处理函数

      3. 冒泡: 由目标元素向外,出发各级父元素上绑定的相同事件处理函数

        目标元素(target): 实际触发事件的元素    

 

   IE8: 2个阶段: 没有捕获阶段  

 

   事件对象: event: 在事件发生时,自动创建的封装所有事件信息的对象

       event对象提供了操控事件的方法: 阻止事件, 取消冒泡....

   何时: 1. 为了获得事件相关的数据

            2. 操控事件

   如何:

      获取: DOM标准: 事件对象,默认作为事件处理函数的第一个参数传入:

               IE8: 事件对象自动保存在全局变量event中

         兼容: function eventHandler(e){

              e=e||window.event;

                  }

     

   取消冒泡: e.stopPropagation();

   利用冒泡:

   优化: 尽量少的添加EventListener

       为什么: 每个EventListener都是一个对象

          浏览器触发事件时,会轮询每个EventListener对象

          添加的EventListener越多,页面响应速度越低

   解决: 当多个平级子元素绑定相同的事件处理函数时

           其实,只要在父元素绑定一次,所有子元素共用即可!

   难题: 1. 如何获得目标元素:

              this->父元素 X

              DOM: e.target ->目标元素

              IE8: e.srcElement

                  兼容: var target=e.target||e.srcElement;

            2. 鉴别目标元素是否想要:

              判断元素的名称或属性

             

 

以上是关于DOM的主要内容,如果未能解决你的问题,请参考以下文章

React虚拟dom中的key值

DOM事件: DOM事件级别DOM事件流DOM事件模型DOM事件捕获过程自定义事件

虚拟DOM(Virtual Dom) VS 影子DOM(Shadow Dom)

虚拟DOM(Virtual DOM)

关于DOM级别的一些问题,DOM0,DOM1,DOM2

DOM事件类