Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOM进阶之Node #189

Open
FrankKai opened this issue Mar 19, 2020 · 0 comments
Open

DOM进阶之Node #189

FrankKai opened this issue Mar 19, 2020 · 0 comments
Labels

Comments

@FrankKai
Copy link
Owner

FrankKai commented Mar 19, 2020

  • Node interface概览
  • Node Properties
  • Node Methods
  • 常见疑问
    • Node.append()与Node.appendChild()区别是什么?
    • Node.textContent,Node.nodeValue区别是什么
    • Node.textContent,Node.innerText的区别是什么?
    • Node.innerText,Node.outerText的区别是什么?
    • Node.innerHTML,Node.outerHTML的区别是什么?
    • Node.className,Node.classList的区别是什么?
    • Node.offsetWidth,Node.scrollWidth,Node.clientWidth的区别是什么?
  • 参考资料

Node interface概览

  • Node interface是众多DOM API对象都会继承的一个interface。
  • 所有继承了Node interface的API对象,会拥有共同的方法和属性。
  • 以下这些interface都继承了Node interface:Document,Element,Attr,CharacterData(Text,Comment,CDATASection继承),ProcessingInstruction,DocumentFragment,DocumentType,Notation。
  • 如果方法和属性在某些情况下没有意义,这些interface会返回null。
  • Node interface继承自EventTarget。EventTarget <- Node。

Node Properties

  • Node.baseURI 由协议:域名/路径组成
  • Node.childNodes live NodeList,当Node的子节点发生变化时,childNodes自动更新
  • Node.firstChild node的第一个child node
  • Node.isConnected node直接或间接连接到context object。如果通过createElement创建出来,没有append/appendChild的话,isConnected是false。
  • Node.lastChild 返回node的最后一个child node。
  • Node.nextSibling 返回node的下一个兄弟节点。
  • Node.nodeName Node的名字。document.nodeName// #document, div // DIV, audio // AUDIO, document.createTextNode('foo') // #text
  • Node.nodeType Node的类型。ELEMENT_NODE 1; TEXT_NODE 3; COMMENT_NODE 8;
  • Node.nodeValue 返回或设置当前node的值。
  • Node.ownerDocument 返回当前node归属的document。
  • Node.parentNode 返回当前node归属的parent Node。
  • Node.parentElement 返回当前node归属的parent Element。
  • Node.previousSibling 返回当前node的上一个兄弟节点。
  • Node.textContent 返回或设置一个标签和它的所有子节点的textual content。

Node Methods

  • Node.appendChild() 可以把指定node添加为当前node的最后一个child。如果指定node在当前DOM树上存在,那么它会从之前位置移除,插入到新的位置。
  • Node.cloneNode([deep]) deep为true时,会连带它的children node都克隆过来。 false时只克隆选中的node。默认为false。

...

基本都是一些增删盖查节点的操作。

常见疑问

  • Node.append()与Node.appendChild()区别是什么?
  • Node.textContent,Node.nodeValue区别是什么
  • Node.textContent,Node.innerText的区别是什么?
  • Node.textContent, Node.innerHTML的区别是什么?
  • Node.innerText,Node.outerText的区别是什么?
  • Node.innerHTML,Node.outerHTML的区别是什么?
  • Node.className,Node.classList的区别是什么?
  • Node.offsetWidth,Node.scrollWidth,Node.clientWidth的区别是什么?

Node.append()与Node.appendChild()区别是什么?

  • ParentNode.append()可以添加DOMString objects,DOMString objects插入后等价Text nodes;Node.appendChild()只接受Node objects。
  • ParentNode.append()没有返回值,Node.appendChild()返回被添加的Node object,比如下面的span标签。
  • ParentNode.append()可以添加多个nodes和strings,需要用逗号分隔。Node.appendChild()一次只能添加一个Node,传入多个Node不会报错,会只添加第一个Node。
var pTag = document.createElement('p');
var spanTag = document.createElement('span');
var str = "foo bar baz";

pTag.appendChild(spanTag);// <p><span></span></p>
pTag.appendChild(str);// Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.

pTag.append(spanTag);// <p><span></span></p>
pTag.append(str);// <p>foo bar baz</p>
pTag.append(spanTag, str);// NodeList [<span>, #text "foo bar baz"]

Node.textContent,Node.nodeValue区别是什么

Node.nodeValue一般都为null。
Node.textContent是当前节点的所有子节点的文本内容,iframe内的标签也会以DOMString的形式出现。

Node.textContent,Node.innerText的区别是什么?

Node.textContent,Node.innerText的区别是什么?

Node.innerText代表渲染过的当前节点和它的子孙节点的文本内容。style标签会生效,标签属性会生效。
textContent则是未渲染的,比如说style,带属性的普通标签会被忽略,直接取全部的文本节点。

<li id="foo">
    文本内容
    <span style="display:none">噢噢</span>
</li>
const domFoo = document.getElementById("foo");
domFoo.innerText; // 文本内容
domFoo.textContent; // 文本内容噢噢

Node.textContent, Node.innerHTML的区别是什么?

textContent返回的是raw数据。
innerHTML返回的是HTML entites。

比如说如果<div><span>的文本子节包含&,>,<,textContent会正常返回,而innerHTML则返回”&amp“,”&lt“,”&gt;“。

Node.innerText,Node.outerText的区别是什么?

Node.innerText,Node.outerText的区别是什么?

Node.innerText代表的是渲染过的当前节点和它的子孙节点的文本内容。

  • 作为getter时,innerText与outerText的值相同。
  • 作为setter时,innerText是正常的替换内容。outerText的值会把当前的node移除,替换成设置的文字。
  • outerText是一个非标准的方案,需要谨慎使用。

stackoverflow上有一个很好的例子可以说明:What is the difference between innerText and outerText?
innerText changes only text within HTML tags, e.g.

<div>
  <p>Change Me</p>
</div>

p.innerText = "Changed!"

Becomes

<div>
  <p>Changed!</p>
</div>

Whereas outerText:

<div>
  <p>Change Me</p>
</div>

p.outerText = "Changed!"

Becomes

<div>
   Changed! <!--可以看到这里的p标签已经被移除了,只留下了Changed!-->
</div>

虽然上面的例子说明了,Node.outerText的作用,但是还不够明显。

这样会更加明显:

<div>
  <p>Change Me</p>
</div>

div.innerText = "Changed!"

Becomes

<div>
  Changed!
</div>

Whereas outerText:

<div>
  <p>Change Me</p>
</div>

p.outerText = "Changed!"

Becomes

   Changed! <!--可以看到这里,不仅p标签已经被移除了,而且div标签也被移除了,只留下了Changed!-->

innerText的setter具有一定的破坏力,但是outerText的setter破坏力更大。
总而言之,在使用innerText和outerText的setter时,一定要谨慎要小心。

Node.innerHTML,Node.outerHTML的区别是什么?

  • innerHTML是Element的一个属性,用来获取或设置包含在元素内部的HTML或XML标签。
  • outerHTML非常强大,可以get序列化的HTML片段,也可以直接set。set的功能可以用于replace一个DOM节点。

举两个例子:

获取DOM字符串:

<div id="d">
  <p>Content</p>
  <p>Further Elaborated</p>
</div>
var d = document.getElementById("d");
console.log(d.outerHTML);

// The string '<div id="d"><p>Content</p><p>Further Elaborated</p></div>'
// is written to the console window

设置DOM字符串:

<div id="container">
  <div id="d">This is a div.</div>
</div>
var container = document.getElementById("container");
var d = document.getElementById("d");

console.log(container.firstChild.nodeName); // logs "DIV"

// 这一行非常非常有用,一定要牢记
d.outerHTML = "<p>This paragraph replaced the original div.</p>";

console.log(container.firstChild.nodeName); // logs "P"

// The #d div is no longer part of the document tree,
// the new paragraph replaced it.

Node.className,Node.classList的区别是什么?

  • className是元素的class名,直接通过el.className get set即可,比Element.getAttribute和Element.setAttribute更好。class是一个HTML Attribute,className是一个DOM Property
  • classList是一个read-only属性,返回一个DOMTokenList,它代表的是元素的class属性几何。可以用来操作class列表。有add,remove,toggle,forEach,replace等等方法。
// item的class名称
let el = document.getElementById('item');

if (el.className === 'active'){
  el.className = 'inactive';
} else {
  el.className = 'active';
}
const div = document.createElement('div');
div.className = 'foo';

// our starting state: <div class="foo"></div>
console.log(div.outerHTML);

// use the classList API to remove and add classes
div.classList.remove("foo");
div.classList.add("anotherclass");

// <div class="anotherclass"></div>
console.log(div.outerHTML);

// if visible is set remove it, otherwise add it
div.classList.toggle("visible");

// add/remove visible, depending on test conditional, i less than 10
div.classList.toggle("visible", i < 10 );

console.log(div.classList.contains("foo"));

// add or remove multiple classes
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");

// add or remove multiple classes using spread syntax
const cls = ["foo", "bar"];
div.classList.add(...cls); 
div.classList.remove(...cls);

// replace class "foo" with class "bar"
div.classList.replace("foo", "bar");

Node.offsetWidth,Node.scrollWidth,Node.clientWidth的区别是什么?

  • offsetWidth 返回元素的layout宽度,包括border,padding和滚动条。不包括伪元素::before或::after的宽度。假设display为none了,那么这个值为0。
  • scrollWidth 是元素的content的宽度。**包括因为overflow导致的不可视区域。(这一点很重要)包含padding, **不包含border,margin和滚动条。包括伪元素::before或::after的宽度。若没有滚动条就能撑满时,scrollWidth与clientWidth宽度是一样的。
  • clientWidth对于行内元素和没有css的元素是0。否则是宽度,包含padding。 不包含border,margin和滚动条。

image

image

规范中的定义:

The offsetWidth attribute must return the result of running these steps:
If the element does not have any associated CSS layout box return zero and terminate this algorithm.
Return the border edge width of the first CSS layout box associated with the element, ignoring any transforms that apply to the element and its ancestors.

The scrollWidth attribute must return the result of running these steps:
Let document be the element’s node document.
If document is not the active document, return zero and terminate these steps.
Let viewport width be the width of the viewport excluding the width of the scroll bar, if any, or zero if there is no viewport.
If the element is the root element and document is not in quirks mode return max(viewport scrolling area width, viewport width).
If the element is the HTML body element, document is in quirks mode and the element is not potentially scrollable, return max(viewport scrolling area width, viewport width).
If the element does not have any associated CSS layout box return zero and terminate these steps.
Return the width of the element’s scrolling area.

The clientWidth attribute must run these steps:
If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
If the element is the root element and the element’s node document is not in quirks mode, or if the element is the HTML body element and the element’s node document is in quirks mode, return the viewport width excluding the size of a rendered scroll bar (if any).
Return the width of the padding edge excluding the width of any rendered scrollbar between the padding edge and the border edge, ignoring any transforms that apply to the element and its ancestors.

https://www.w3.org/TR/2016/WD-cssom-view-1-20160317/#dom-element-clientwidth

参考资料

https://developer.mozilla.org/en-US/docs/Web/API/Node
https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append
https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild
https://developer.mozilla.org/en/docs/Web/API/Node/textContent
https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeValue
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/outerText
https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML
https://developer.mozilla.org/en-US/docs/Web/API/Element/className
https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollWidth
https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant