Text property in script tags - Clarification?

匿名 (未验证) 提交于 2019-12-03 01:05:01

问题:

While reading angular's directives code , I saw this :

var scriptDirective = ['$templateCache', function($templateCache) {   return {     restrict: 'E',     terminal: true,     compile: function(element, attr) {       if (attr.type == 'text/ng-template') {         var templateUrl = attr.id,             text = element[0].text;// <-- Look here          $templateCache.put(templateUrl, text);       }     }   }; }]; 

But I didn't know what is that text property ( I mean ― why not use innerText ?)

I was told that :

"it's like textContent just only grabs the text nodes inside the element, no recursion or the likes"

Also looking at the docs :

The IDL attribute text must return a concatenation of the contents of all the Text nodes that are children of the script element (ignoring any other nodes such as comments or elements), in tree order. On setting, it must act the same way as the textContent IDL attribute.

Wasn't clear for me.

Mdn's :

text:Like the textContent attribute, this attribute sets the text content of the element. Unlike the textContent attribute, however, this attribute is evaluated as executable code after the node is inserted into the DOM.

So I created a test :

<script id="a"   type="blabla">  foo    <b>bar</b>    baz </script>    <script >  console.log(document.getElementById('a').text)  console.log(document.getElementById('a').textContent) </script> 

But both shows the exact content :

"  foo    <b>bar</b>    baz " 

Question:

  • Why does angular uses text and not textContent ? if it's a template - then they do need to consider tags....no ?

  • What is the difference (in script tags) between innerText/text/textContent ?

BTW there is a similar question here , but it doesn't talk much about the script scope (which is actually a must in my question)

回答1:

Here's a fork of your jsbin where you can see the difference: http://jsbin.com/tovipiruce/1/edit?html,js,output

Or, if you're a snippets fan:

var scriptElem = document.getElementById('a');  var child = document.createElement('b'); child.textContent = 'Look at me! I am irrelevant!';  var comment = document.createComment('I contain a lot of wisdom'); var justText = document.createTextNode('just your average text node');  scriptElem.appendChild(child); scriptElem.appendChild(comment); scriptElem.appendChild(justText);  console.log(scriptElem); console.log('textContent:', scriptElem.textContent); console.log('innerText:', scriptElem.innerText); console.log('text:', scriptElem.text);
<!DOCTYPE html> <html> <body>    <p>Open your console</p>  <script id="a" type="blabla"> foo <b>bar</b> baz </script>  </body> </html>

The Big Difference here is how child elements are handled: textContent includes the child elements, so that the output will contain Look at me! I am irrelevant!, while text will not.

I'll repeat that in code:

scriptElem.textContent.includes('Look at me!'); // true scriptElem.text.includes('Look at me!'); // false 

The Getter

Let's look at a very naive implementing of the getters of textContent and text:

function textContent(elem) {     return Array.from(elem.childNodes).map(node => {         // recurse into element nodes         if (node.type === Node.ELEMENT_NODE) {             return textContent(elem);         }          // return the value of text nodes         if (node.type === Node.TEXT_NODE) {             return node.nodeValue;         }          // and ignore everything else         return '';     }).join(''); }  function text(elem) {     return Array.from(elem.childNodes).map(node => {         // return the value of text nodes         if (node.type === Node.TEXT_NODE) {             return node.nodeValue;         }          // and ignore everything else         return '';     }).join(''); } 

As you can see (and as the specification says and the example shows), only text nodes are handled when getting an element's text property, while textContent also throws into the mix the textContent of its element children.

innerText is a more complex beast which won't be explained in this answer; it's like a normalized textContent. You can read more about it in this fabulous blog post by Kagnax.

The Setter

Now let's talk about the setter. The specification says it should behave the same way as setting textContent, but mdn says the following bizarre thing:

Unlike the textContent attribute, however, this attribute is evaluated as executable code after the node is inserted into the DOM.

There're two ways to interpret this sentence: Either that setting a script's textContent before injecting it into the page has no effect while setting text does, or that after injecting it into the page setting textContent has no effect but setting text does.

Testing on latest Chrome (47) and Firefox (43) shows that both interpretations are false: setting a textContent before injection works, and setting text after injection has no effect. If someone has an IE lying around and wishes to test this, I'd appreciate if you edit this answer.

...but why?

So we've gone through the setter and the getter. Now let's ask why is text useful? That's an open question. Frankly, I don't really know. As you've seen in your original code, you can't just insert markup in a script tag, it's not parsed as html. So the only way to see a difference is when you dynamically inject nodes inside a script tag.

I ran git blame on that file, and saw that it came from this commit:

fix(script): Incorrectly reading script text on ie

IE deals with script tags in special way and .text() does not work. Reading the .text property directly fixes the issue.

The added test case does a binding inside the script tag. I don't know angular so I don't know what that entails, nor do I have IE so I can't check what happens in the test case when you use textContent instead of text.

But I couldn't help but smile when I saw that IE was still alive and kickin'.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!