• 从JS和jQuery浅谈DOM操作,当我们在获取时,究竟获取了什么


    0、写在前面的话

    自己对前端的东西一直不是很熟,现在开始要想办法从前端各个地方去获取想要的属性值的时候,也基本是在网上现炒现卖,几周下来,发现自己还是迷迷糊糊,可以算是一无所获。

    所以就抽时间,把这一块的东西收集整理一下,免得每次为了得到一个值要上网查询鼓捣一万年,实在是浪费时间,知其然知其所以然,才能让问题迎刃而解。

    这篇博文应该说结构还不够完整,有些知识点还没提到,覆盖的内容其实主要还是为了满足我现在入门知识理解的一个需求,日后再进行完善吧,这里先贴一些相关概念的参考链接,以后方便参考和添加:

    1、理解节点

    HTML文档中,所有内容都是 “节点”,是一个Element,这意味着:
    • 整个文档是一个文档节点
    • 每个 HTML 元素是元素节点
    • HTML 元素内的文本是文本节点
    • 每个 HTML 属性是属性节点
    • 注释是注释节点

    先看下面的片段:
    <html> 
      <head>
        <title>DOM 教程</title>
      </head>
      <body>
        <h1>DOM 第一课</h1>
        <p>Hello world!</p>
      </body>
    </html>
    从上面的 HTML 中:
    • <html> 节点没有父节点;它是根节点
    • <head> 和 <body> 的父节点是 <html> 节点
    • 文本节点 "Hello world!" 的父节点是 <p> 节点
    并且:
    • <html> 节点拥有两个子节点:<head> 和 <body>
    • <head> 节点拥有一个子节点:<title> 节点
    • <title> 节点也拥有一个子节点:文本节点 "DOM 教程"
    • <h1> 和 <p> 节点是同胞节点,同时也是 <body> 的子节点
    并且:
    • <head> 元素是 <html> 元素的首个子节点
    • <body> 元素是 <html> 元素的最后一个子节点
    • <h1> 元素是 <body> 元素的首个子节点
    • <p> 元素是 <body> 元素的最后一个子节点

    重点:

    2、JS获取节点

    我们知道,HTML文档载入浏览器后,会成为Document对象,这个对象让我们可以从脚本中对HTML页面所有元素进行访问,我们经常获取元素使用的几大方法:getElementById()、 getElementsByClassName、getElementsByName()、getElementsByTagName()就归属其下,也是我们最常使用到的方法。

    • getElementById() 根据id属性
    • getElementsByName() 根据name属性
    • getElementsByClassName 根据class的名称
    • getElementsByTagName() 根据标签

    2.1 获取单一节点

    我们用document.getElementById(id)这个方法举例,因为是根据HTML中唯一的id来获取,所以得到的是单一节点,这个节点实际就是一个对象,也就是说,这个方法返回的是一个HTML对象。

    每个标签的出现,都意味着有一个对应的对象被创建,比如 <select> 标签的存在,意味着有一个 Select对象 诞生;<option> 标签,意味着有一个 Option对象 诞生。

    我们说过,每个节点,都是一个对象,是对象,就有属性,所以我们想要获取对应的值,你只要找到了这个对象,完全可以到w3school上查看下这个对象有哪些属性,直接调用就可以了。

    实际上,很多属性是大部分对象都有的,也是我们所常用的:
    • id 该节点的id值
    • value 该节点的value值
    • text 节点的文本值
    • name 节点的name,名称

    诸如此类,还有很多,具体的可以直接查询手册。

    来看看示例,假如有下面的HTML片段,现在想获取关于 “张三” 的各类信息:
    <table class="manage-table space" id="dataTable">
    	<tr class="center">
            <th rowspan="2">学号</th>
    		<th rowspan="2">姓名</th>
    		<th rowspan="2">性别</th>
    		<th colspan="3">班级</th>
            <th colspan="2">选课情况</th>
    	</tr>
        <tr class="center">
            <th>类别</th>
    		<th>学届</th>
    		<th>班号</th>
            <th>选课数</th>
        </tr>
        <tr class="center" idx="89">
            <td id="studentId" hidden="true">89</td>
            <td>0001</td>
            <td>张三</td>
            <td><a>男</a></td>
            <td id="classId" hidden="true">38</td>
            <td>理科</td>
            <td>1999</td>
            <td>3</td>
            <td>3</td>
        </tr>
    </table>

    先通过之前提到的,可以通过查看网上的文档和浏览器的调试器来找元素的”位置“,最终通过遍历定位到该行 tr :
    var dataTable = document.getElementById("dataTable");
    var zhangsan;
    for(var x in dataTable.rows) {
        for(var y in dataTable.rows[x].attributes) {
            if(dataTable.rows[x].attributes[y].name === "idx"
                    && dataTable.rows[x].attributes[y].value === "89") {
                zhangsan = dataTable.rows[x];
            }
        }
    }

    写到这里,需要注意一点,那就是 for in 这个循环遍历,这个方法是将对象的所有属性遍历,我们知道,数组也是一个对象,索引视为其属性,所以假如数组还有额外的属性,遍历会将该属性输出。

    所以,上面的写法实际上存在很大的问题,因为Array表面上看来只有索引,实际上因为原型继承的关系,它还有很多从父类继承下来的属性,在这里也会同时遍历出来,如索引遍历完后紧接着遍历了其原型对象的属性 item,这个属性就没有attributes属性的,但是这里之所以没有报错,是因为JS的灵(缺)活(陷),直接表示出undefined,所以也在我们的if条件判断中溜了过去。

    正确的写法怎么写?就老老实实用for循环,后面的例子会再次提到,你只要知道这样写是存在很大风险的,不要这样做,然后继续往下看就好。

    另,ES6中引入的iterable类型对象可以使用 for of 方法规避这个问题,而且直接遍历出元素,而不是索引这么麻烦。

    回到正题,现在我们得到了这一排,也就是这个 tr,那么什么信息都可以得到了,比如每个单元格的文本内容:
    for(var z in zhangsan.cells) {
        console.log(zhangsan.cells[z].innerText);
    }

    注意 innerHTML 和 innerText 的区别,比如对应zhangsan性别的那栏单元格:
    innerHTML  --> "<a>男</a>"
    innerText --> "男"

    2.2 获取多个节点

    获取多个节点的方法如 getElementsByClassName、getElementsByName()、getElementsByTagName() 等,其实也很简单,无非是获取到了HTML对象的集合,再根据情况遍历使用就可以了。

    比如:
    var trs = document.getElementsByTagName("tr");
    console.log(trs);
     

    2.3 DOM方式的获取

    上面我们提到的获取元素属性等等的方式,实际上很粗暴,就是使用对象直接获取其属性,实际上,还可以使用DOM方法来获取,而且是有一定区别的。

    假如我们获取到一个HTML对象,叫做htmlObj,现在我们要获取它的id值:
    htmlObj.id;  //对象方式,直接获取属性
    
    htmlObj.getAttribute("id");  //DOM方法

    如果你在浏览器调试的时候,没有发现获取的HTML对象有类似getAttribute()这种方法,别着急,如果你知道原型的概念,你可以顺着它的 __proto__ 属性延伸一直摸索,看到一个 ElementPrototype 的时候,这些方法就在里面。现在不理解没关系,你只要知道这些方法就是HTML DOM对象从上面继承下来的,并不是你没看见就没有

    用实际点的例子来说明,还是接刚才zhangsan的HTML作为例子,这次不磨叽,先拿到zhangsan看下属性:
    var dataTable = document.getElementById("dataTable");
    console.log(dataTable);
    
    var zhangsan = dataTable.rows[2];
    console.log(zhangsan);

    通过控制台的输出,我们可以看到,对于一些常见的属性,比如id、class等,我们可以确实可以使用 zhangsan.id 或者 zhangsan.className 来获取得到。 可是我们的zhangsan还额外自定义了一个属性idx,那么问题就来了。

    问题就出在,这个idx属性并没有直接放在zhangsan这个对象下,而是作为attributes集合中的元素,所以之前我们没有办法直接使用如 zhangsan.idx 的方法获取值,而是很麻烦地再次通过遍历和条件判断来进行定位。

    而实际上,DOM对象有一个 getAttribute() 方法,可以通过属性名来获取属性的值,所以之前我们定义zhangsan的方法,实际上可以改成这样:
    var dataTable = document.getElementById("dataTable");
    console.log(dataTable);
    
    var zhangsan;
    
    for(var x in dataTable.rows) {
        var person = dataTable.rows[x];
        if(person.getAttribute("idx") === "89") {
            zhangsan = person;
        }
    }
    
    console.log(zhangsan);

    很不幸,会报出 person.getAttribute() is not a function 的错误,why?之前提到过,for in 会遍历其所有属性,在遍历完索引以后,它继续遍历那些从父类继承下来的属性,某些属性并不是DOM对象,是没有getAttribute方法的,循环到这里的时候,自然就报错了。

    所以我们还是老老实实使用for循环:
    for(var x = 0; x < dataTable.rows.length; x++ ){
        if(dataTable.rows[x].getAttribute("idx") === "89") {
            zhangsan = dataTable.rows[x];
        }
    
    }

    或者把之前的方法改一下,找到了以后就让循环break,不再遍历后面的元素:
    for(var x in dataTable.rows) {
        var person = dataTable.rows[x];
        if(person.getAttribute("idx") === "89") {
            zhangsan = person;
            break;
        }
    }

    或者把继承的属性筛掉不要:
    for(var x in dataTable.rows) {
        if(dataTable.rows.hasOwnProperty(x)){        
            var person = dataTable.rows[x];
            if(person.getAttribute("idx") === "89") {
                zhangsan = person;
            }
        }
    }

    这下,就正确了,代码也简单了好多,就是利用DOM方法,所以在特别是使用一些自定义标签的时候,使用DOM的方法可以更加便捷。


    3、jQuery获取

    3.1 认识jQuery的基本概念

    我们都知道jQuery是一个JS函数库,我们也知道jQuery可以更少的代码做更多的事,我们还知道用jQuery获取节点都是通过 $(selector) 的方式。可是,这里的$号什么意思呢?

    JS世界中,变量命名的规范里,$是属于合法的标识符的,jQuery的函数有两个名字,一个叫 jQuery,另一个就叫 $。所以实际上 $.ajax(options) 和 jQuery.ajax(options) 是等同的,你可以用浏览器的调试器,到控制台中去输入 $===jQuery,结果会得到 true。

    我们都知道JS中函数也是一个对象,所以这里的 $ 表示函数本身,如果是 $(),那么根据函数中return,你就会得到一个对象,我们常说是一个jQuery对象。

    现在你不需要去深究太多,你只用知道常见的jQuery语法最终就是得到一个jQuery对象,这个对象看似和你通过document.getElementById之类的方法得到的东西一样,实际上并不是,内容相似但完全是两种不同的对象。所以,jQuery对象没办法使用HTML DOM对象中的方法,比如 getAttribute ,同理HTML DOM对象也无法使用jQuery对象的方法

    不过不用担心,jQuery中封装了大量的方法,可以替代HTML对象中的很多操作。比如 $("#foo").html(); 等同于 document.getElementById("foo").innerHTML;

    3.2 jQuery对象和DOM对象的相互转换

    上面已经提到了,两者是不同的对象,方法也不能共通,但是他们两者之间是可以进行相互转换的。

    jQuery对象 --> DOM对象
    var $cr=$("#cr"); //jquery对象
    var cr = $cr[0]; //dom对象 jQueryObj[0]也可写成 jQueryObj.get(0);
    alert(cr.checked); //检测这个checkbox是否给选中

    DOM对象 --> jQuery对象
    //只需要用$()把dom对象包装起来
    var cr=document.getElementById("cr"); //dom对象
    var $cr = $(cr); //转换成jquery对象

    另外,从规范命名来讲,如果获取的对象是 jQuery对象,那么在变量前面加上$,这样方便容易识别出哪些是jQuery对象

    2018.03.22补充如下
    在项目中遇到了这样的问题,有这样一段代码:
    function loadLimit(trainingId, departmentId) {
        var trs = $("#dataTable tr");
        if (trs.length > 1) {
            for (var i = 1; i < trs.length; i++) {
                trs[i].remove();
            }
        }
        $().invoke("/admin/elite/join/do/loadJoin.q", {trainingId:trainingId, departmentId:departmentId}, function(html) {
            $("#dataTable").append($(html).find("tr"));
        });
    }

    这段代码在Chrome中运行正常,后来在IE中出错,这才发现了问题。当时误以为获取的多标签集合jQuery对象,其子元素也是jQuery对象,所以自然而然使用了remove(),实际上trs[i]获得的已经是DOM对象,不支持jQuery方法,故报错。我试着打印了结果,如下图中,上为DOM对象,下为jQuery对象:
    可以看到,jQuery对象中index为0的子元素,包含典型的innerHTML等属性,这显然是DOM对象的东西。也印证了之前写过的笔记jQueyr对象转DOM对象只需要类似var cr = $cr[0];即可。所以我自己的错误应该这样修改:
    $(trs[i]).remove();

    亦或者有这样的方法eq(),表示将匹配元素集缩减值制定index上的一个:
    trs.eq(i).remove();

    3.3 jQuery的选择器和方法

    jQuery的选择方式和CSS的选择器方式类似,完整版你可以参考这里:(1)jQuery的选择器 (2)jQuery 参考手册 - 选择器
    至于jQuery的方法,还是直接丢链接比较方便:(1)jQuery API 中文文档 (2)w3school的jQuery教程

    3.4 jQuery选择示例

    还是zhangsan的例子,我们再贴一次代码,免得拉上去再看:
    <table class="manage-table space" id="dataTable">
    	<tr class="center">
            <th rowspan="2">学号</th>
    		<th rowspan="2">姓名</th>
    		<th rowspan="2">性别</th>
    		<th colspan="3">班级</th>
            <th colspan="2">选课情况</th>
    	</tr>
        <tr class="center">
            <th>类别</th>
    		<th>学届</th>
    		<th>班号</th>
            <th>选课数</th>
        </tr>
        <tr class="center" idx="89">
            <td id="studentId" hidden="true">89</td>
            <td>0001</td>
            <td>张三</td>
            <td><a>男</a></td>
            <td id="classId" hidden="true">38</td>
            <td>理科</td>
            <td>1999</td>
            <td>3</td>
            <td>3</td>
        </tr>
    </table>

    我们要获得zhangsan,用jQuery的方式就是:
    var zhangsan = $("[idx='89']");  //表示通过属性查找,属性idx为89的

    没错,就是这么简单... 

    然后你要么使用jQuery的一些方法来获取里面的内容,或者粗暴地将它转换为DOM对象再操作。

  • 相关阅读:
    C# 英语纠错 LanguageTool
    WPF TreeView 虚拟化-设置滚动到选中项
    C# 同步更新系统时间
    C# 重置IE安全等级
    C# IE环境
    C# IE环境
    WPF ObservableCollection 异步调用问题
    C# 以函数Action/Func/Task作为方法参数
    WPF Geometry 引用Path数据
    ResourceDictionary主题资源替换(一) :通过加载顺序来覆盖之前的主题资源
  • 原文地址:https://www.cnblogs.com/deng-cc/p/6655074.html
Copyright © 2020-2023  润新知