jquery 取子元素-【Qchat后端】强大的DOM(一)

在本系列的第一篇文章中,我们谈到了什么是“编程”,而Web编程自然需要“编程”和“Web”这两个要素。

后面我已经讲过“编程”的几个要素了。

作为一个程序来说,这已经足够了,但是很多人从学习JavaScript到写网页还是有一个坎——“我学会了句型,我从哪里开始写网页呢?”。

为了让编码能力真正作用于网页并与网页交互,还需要两个东西:DOM和BOM。

JavaScript=ES+DOM+BOM。

BOM——浏览器对象模型(BrowserObjectModel),DOM——文档对象模型(DocumentObjectModel)。

BOM用于承载网页,DOM用于显示网页。

本文的主角是DOM。

什么是 DOM

从性能的角度来说,它决定了网页的内容; 从编码的角度来看,它包括属性和能力。

前一个很好理解,但是前一个也好像在谈恋爱,这不是对象吗? 因此,DOM是网页结构和编程套接字的结合。

需要注意的一点是,虽然前面提到了 JavaScript 等式,但 DOM 并不是 JavaScript 独有的,而是跨平台且与语言无关的。 之所以和JavaScript联系在一起,是因为JavaScript提供了DOMAPI。

任何 HTML 或 XML 文档都可以通过 DOM 表示为由节点组成的层次结构。 节点有多种类型,每种类型对应不同的信息或标签,也有自己的特性、数据和技能,并与其他类型有一定的关系。 这种关系构成了层次结构,使得标签可以表示为特定的以A节点为根的树结构。 它通常被称为 DOM 树。

DOM很强大,但是却一直让人头疼。 使用时间长、版本多、兼容困难、性能消耗大……给人的印象是DOM是个原始又不好的东西,最好远离它。

因此,你谈论更多的是如何“减少”处理原生DOM,比如几年前的Jquery/Zepto,以及近几年的Vue和React。 后者对于DOM操作有很好的兼容和封装,使用起来更加方便。 前者引入了“虚拟DOM”和更合理的diff算法。 代码试图阻止直接操作DOM,而是操作数据,框架是基于数据变化的。 处理从“虚拟 DOM”到“真实 DOM”的转换。

然而,这并不意味着不需要 DOM,也不是人们忽视它的原因。 在某些场景下,操作 DOM 甚至是唯一的方法。 因此,掌握DOM相关知识仍然是现代后端开发应该做的事情。

文档

在浏览器中,文档对象document代表了整个HTML页面,它是window对象的一个​​属性,是一个全局对象。

文件信息

文档对象包含一些页面公共信息,例如:URL、域和引用者。

这类信息可以在请求的HTTP返回信息中获取,但在JavaScript中只能通过这些属性来暴露。

只能设置域属性。 但是,出于安全原因,无法设置未包含在 URL 中的值。

当页面包含或嵌入不同的子域时,设置域非常有用。 由于跨域通信存在安全风险,不同子域的页面之间很难通过JavaScript进行通信。 此时,在每个页面将domain设置为相同的值,就可以访问对方的JavaScript对象了。

文件写作

文档对象具有一种古老且不太常用的功能,可以将内容写入网页。 对应4个方法:write()、writeln()、open()和close()。

其中write()和writeln()方法都接收一个字符串参数,可以将字符串写入网页中。 write() 只是写入文本,而 writeln() 都在字符串末尾附加一个换行符 (n)。 这两种方法可用于在页面加载期间动态添加内容。 如果你想使用脚本尽快在浏览器中输出一些东西,write()是首选。

但需要注意的一点是,页面渲染时可以通过 document.write() 向文档输出内容,但如果在页面加载后调用 document.write() ,输出的内容会重绘整个页面,比如onloadstorm的bounce函数中,要格外小心。

document.write('hello world')

先说整体情况,再看具体节点。

DOM节点

每个页面都是一棵元素树,由很多节点组成,所有节点类型都继承自Node类型,共享基本属性和技能。

节点类型

每个节点都有自己的类型属性,例如:元素节点、文本节点,类型值nodeType与常见节点类型的对应关系如下:

可以通过以下代码获取页面中某个div的节点类型:

let div = document.getElementsByTagName("div")[0];
div.nodeType  //1

需要注意的是,空格和换行符也算作节点,并被视为文本节点。

不仅nodeType、nodeName和nodeValue还可以用于更具体地查询节点,但它们并不总是返回合理的值。 操作前最好先确定节点类型。

节点关系

子节点

childNodes是元素子节点的集合,每个节点都有一个childNodes,其中包含一个NodeList实例。 NodeList 是一个类似字段的对象,用于存储可以按位置访问的有序节点。

NodeList 对象的特殊性在于它是对 DOM 结构的查询,因此 DOM 结构的更改将手动反映在 NodeList 中。

您可以使用两种方法来访问 nodeList 中的元素 - 括号、item()。

let firstChild = parent.childNodes[0]
let firstChild = parent.childNodes.item(0)

大多数开发人员倾向于使用方括号,更像是访问链接列表项。

父节点

每个节点都有一个parentNode 属性,该属性指向其在DOM 树中的父元素。

兄弟节点

jquery获取元素的子元素_jquery取子元素的值_jquery 取子元素

使用 previousSibling 和 nextSibling 在节点之间导航。 第一个节点的 previousSibling 属性为 null,最后一个节点的 nextSibling 属性也为 null。

第一个和最后一个节点

firstChild和lastChild分别指向childNodes中的第一个和最后一个子节点。

借助这个关系指针,几乎可以访问文档树的任何节点,这些便利性就是childNodes的最大亮点。

空的

另一种方法是 hasChildNodes(),它返回 true 表示该节点有一个或多个子节点。 这种方法无疑比查询childNodes的length属性更方便。

元素定位

无论是查询还是操作 DOM,首先需要获取目标元素。 幸运的是,它为开发人员提供了多种技术。

此外,文档对象上还有一些表示特定集合的特殊属性,它们是:

document.anchors:包含文档中所有具有name属性的元素

document.forms:包含文档中的所有元素

document.images:包含文档中的所有图像

元素

document.links:包含文档中所有具有href属性的元素

看到这里,你一定觉得少了点什么。 既然可以通过Id、TagName、Name来定位元素,但是没有类呢? 是的,最初的标准中是没有类定位方法的,直到HTML5版本才引入了类定位元素方法。

另外,还有一个值得关注的地方就是classList属性。 根据业务逻辑和操作类来修改元素的样式是很常见的需求。 HTML5之前,我们可以操作className,但是className是一个字符串,需要分为拆卸、修改,而且形参稍微复杂一些。 classList 提供了更简单、更安全的实现。

用过Jquery的同事应该很熟悉这些操作,但是早年原生的操作起来没那么方便,但是现在可以了。

有了classList属性,除非元素的class属性被完全删除或者重绘,否则className属性将不会被使用。 IE10及以上版本等主流浏览器都实现了classList属性。

既然提到了Jquery,就不得不提到SelectorsAPI。 上述定位元素的方法都是比较传统的。 在新的W3C标准中,提供了类似于Jquery-CSS Query API的选择器定位能力。

无论是label、id还是class,它们都属于CSS选择器。 因此,前些年需要采用三种不同的方式来实施。 新的API只需要一个。 另外,它还支持后代元素的选择,这无疑是一个很大的便利。

应该注意的是,querySelectorAll 返回静态“快照”而不是“实时”查询。 这种低级实现可以防止使用 NodeList 对象可能导致的性能问题。

最后一个成员matches(),它接收一个CSS选择器参数,如果元素与选择器匹配则返回true,否则返回false。

元素遍历

jquery获取元素的子元素_jquery取子元素的值_jquery 取子元素

有时,我们需要遍历子元素并对其进行操作。 每个元素可以有任意数量的子元素和后代元素,并且元素本身也可以是其他元素的子元素。

childNodes 属性包含该元素的所有子节点,这些子节点可能是其他元素、文本节点、注释或处理指令。 不同的浏览器在识别这些节点时有显着不同的行为。

这是一个经典的例子:


        

  •     

  •     


解析上面的代码时,ul元素将包含7个子元素,3个是li元素,以及4个Text节点(li元素周围的空格)。 如果元素之间的空格被删除,所有浏览器将返回相同数量的子节点。

在这些情况下操作元素节点,以前我们必须通过nodeType来判断节点的类型,然后进行后续的操作。

现在有两个新选项;

1. 元素遍历规范

ElementTraversalAPI 为 DOM 元素添加 5 个属性:

2. 儿童

孩子和孩子节点有什么区别?

你没看错,措手不及是一道笔试题,笔者以前遇到过,你能不往下看就答出来吗?

既然有了childNodes,就可以获取该元素的所有子元素了,为什么还需要children呢。 事实上,children属性的出现正是IE9之前的版本与其他浏览器在处理空白文本节点时的区别。

Children 属性是一个仅包含该元素类型的子节点的 HTMLCollection。 相当于帮助开发者过滤掉其他类型的节点。

在现代浏览器中,所有 DOM 元素都将具有此属性。 有了ElementTraversalAPI和children属性,开发者不用担心节点选择错误导致的错误操作。

操作节点

定位和遍历都是为了寻找节点,可以称之为“查”。 要与 DOM 交互,您必须能够“添加、删除和修改”。

创建 DOM 最常见的方法是创建元素,如下所示:

let newNode = document.createElement('div')

在childNodes列表的末尾添加一个节点,appendChild()方法返回新添加的节点。

let resNode = parentNode.appendChild(newNode)

需要注意的是,如果将文档中已经存在的节点传递给appendChild(),那么该节点将会从之前的位置转移到新的位置,即一个节点不会在文档中的多个地方出现同一时间。

如果要将节点放置在 childNode 中除末尾之外的特定位置,可以使用 insertBefore() 技巧。 该方法接受两个参数:要降低的节点和参考节点。 调用该方法后,要降低的节点将成为引用节点的前一个兄弟节点并返回。

jquery 取子元素_jquery取子元素的值_jquery获取元素的子元素

let resNode = parentNode.insertBefore(newNode,targetNode)

既然有前自增,那么如何实现后自增呢? 这也是一道经典的笔试题~

appendChild() 和 insertBefore() 在插入节点时不会删除任何现有节点。 相反,replaceChild()方法接受两个参数:要插入的节点和要替换的节点。 返回要替换的节点并从文档树中完全删除,并替换要插入的节点。

let resNode = parentNode.replaceChild(newNode,targetNode)

要删除节点而不是替换它,请使用removeChild() 方法。 此方法采用一个参数,即要删除的节点。 被删除的节点将被返回。

let resNode = parentNode.removeChild(oldNode)

所有节点类型也共享两种模式。 第一个是cloneNode(),它返回一个与调用它的节点完全相同的节点。 cloneNode() 方法接收一个布尔参数,指示是否进行深复制。 当传入 true 参数时,会进行深拷贝,即复制该节点及其整个子 DOM 树。 如果传递 false,则仅复制调用此方法的节点。

let resNode = targetNode.cloneNode(true)  //深复制目标节点

ps:cloneNode()方法不会复制添加到DOM节点的JavaScript属性,例如风暴处理程序,仅复制HTML属性,并且可以选择复制子节点,并且不会复制其他任何内容。

最后一种方法是normalize()。 它唯一的任务是处理文档子树中的文本节点。

由于解析器实现或 DOM 操作的差异,可能存在不包含文本的文本节点,或者文本节点彼此是兄弟节点。 在一个节点上调用normalize()方法会检查该节点的所有后代jquery 取子元素,搜索上述两种情况。 如果发现空文本节点,则将其删除; 如果两个兄弟节点相邻,则将它们合并为一个文本节点。

说到这里,你会发现上面的操作更适合单个特定元素,不适合批量操作。 与先创建一堆节点,然后按正确的顺序连接它们相比,直接插入 HTML 字符串要容易(更快)得多,即innerHTML 属性。

当浏览器读取innerHTML属性时,它返回该元素的所有后代的HTML字符串,包括元素、注释和文本节点。 在编写innerHTML时,元素中原来包含的所有节点都会根据提供的字符串值替换为新的DOM子树。

let newNode = document.createElement('div')
newNode.innerHTML = '
'

在写入模式下,分配给innerHTML属性的值被解析为DOM子树并替换该元素之前的所有节点。

赋值的值默认为HTML,其中的所有标签都会被浏览器处理后转换为HTML形式的元素。 如果形参不包含任何HTML标签,则直接生成文本节点。

优点:这些技术的优点是插入大量html比较方便。 HTML解析器是浏览器中的底层代码(通常是C++),它比JavaScript快得多。

缺点:频繁操作会导致性能损失(可以先创建一个字符串,然后将操作合二为一进行优化); 被替换的元素,如果有绑定的storm handler或者其他对象属性,这些关系会卡在显存中(最好在操作前自动删除); 它可能给用户留下攻击xss的机会(用户输入的数据阻止以这种方式注入到网页中)。

说完了大的操作,接下来看看节点本身的操作。

我们将它们分为两类 - 修改属性和更改样式。

虽然风格也是属性之一,但是两者在操作目的上却略有不同。

我们先来谈谈风格。 元素的样式可以是外部样式表或内联样式。 如果是外部样式,一般是通过操作类来改变,也就是上面提到的classList。 如果是内联样式,可以通过style属性来改变。 是这样的:

var div = document.getElementsByTagName("div")[0];
    div.style.width="100px";
    div.style.height="100px";
    div.style.backgroundColor="red";

jquery取子元素的值_jquery获取元素的子元素_jquery 取子元素

先获取目标节点,然后通过点操作方法修改具体样式。 需要注意的是,CSS中以分割线“-”表示的属性必须替换为驼峰式大小写(示例中的backgroundColor)。

如果想一次性批量修改,也可以使用cssText属性,速度比较快。

div.style.cssText = 'width:100px;height:100px;backgroud-color:red'

然而,风格有一个缺陷。 在阅读状态下,只能获取行内样式,很难获取真实表达的样式。 哪些是真实的表现? 也就是说,元素在受到多个来源的样式规则影响时的显示方式。

有一个方法可以帮助我们做到这一点——getComputedStyle,它是一个全局方法,使用方法如下:

getComputedStyle(ele,'可选参数')

此方法接受两个参数:要设置样式的元素和伪元素字符串(例如“:after”)。 如果不需要查询伪元素,第二个参数可以传null。 getComputedStyle() 方法返回元素的估计样式。

由于估计的样式是所有样式相加的结果,甚至还包含了浏览器内部样式表中的信息,所以看起来会好很多。 如果您只需要获取某些项目的值jquery 取子元素,则可以使用点运算符。 这里我就不重复了。 。

关于样式操作,日常使用就这些了,接下来再看样式以外的属性。

其实没有什么特别的,无非就是get和set而已。

普通属性也是属性,所以可以使用点运算。 例如,我们收到了一个输入框元素并将其设置为禁用状态,就是这样。

let ipt = document.querySelector('ipt')
    ipt.disabled = true

let ipt = document.querySelector('ipt')
    ipt.getAttribute('disabled')  // false
    ipt.setAttribute('disabled',true)
    ipt.getAttribute('disabled')  // true

总结

内容已经足够多了,但这只是完整 DOM 系统的冰山一角。 文章列出了一些常见且常用的。

本文的标题是强大的 DOM,因为网页上几乎所有内容都与它有关。 掌握了它就相当于掌握了网页的“脉搏”,需要哪些元素,如何修改元素,如何获取。 具体元素所包含的数据和能力,如何与网页更加友好的交互,都是基于对DOM的深入掌握。

即使当前的编程趋向于框架化、模板化和数据驱动,它也不可能包含所有内容。 如果框架没有这个能力怎么办? 如果我们想使用组件库中的特定功能怎么办? 追根溯源,可以听到一些DOM的影子,这是基础。

由于接触较多,本节分为上下两部分,但不会涵盖所有内容。 欢迎进一步交流。

系列文章:

现在各类框架和工具都在“横行”,到处都在讲原理、讲源码,甚至还有跨端技术需要我们去探索,但如果基本功不好,学哪个就事半功倍结果事倍功半,疗效很差,费时费力,打击自信心。 本文是【轻聊后端】系列的第(十一)篇,我打算系统性、逻辑性地与大家分享原生 JavaScript 知识,帮助大家更轻松地理解知识体系,更好地理解和记忆,我尽力生活达到预期。

请喝一杯,记住三遍~

1.看完记得给江老师点个赞,就有动力

2.关注公众号后台轶事,和你聊聊后台轶事

3.文章已收录在GithubfrontendThings谢谢Star✨