Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

innerHTML and  

I have a weird JavaScript behavior which I cannot explain.

Consider this code:

var el = document.createElement('div')
var s = String.fromCharCode(160)

el.innerHTML = s

console.log(s)            // prints space
console.log(el.innerHTML) // prints " "

Now I know that   is non-breaking space, but technically, I just made an assignment of one variable to another, both are strings. How come the values are different?

Is innerHTML a special kind of string or something?

How does it work? What Is the mechanism which translates this?

like image 909
Yossi Vainshtein Avatar asked Dec 10 '17 09:12

Yossi Vainshtein


2 Answers

innerHTML is not a simple property, it's an accessor property with getter and setter. Means that under the hood functions work. innerHTML serializes the given input into HTML. It parses the given value, creates elements and nodes, then appends those ones to the element.

It is defined like this

const DOMElement = {
  value: null,
  set innerHTML(html) {
     console.log(`Setting html: ${html}`);
     // Translate into elements and nodes, then append to the element
     this.value = html;
  },
  get innerHTML() {
     console.log(`Getting html:`);
     // Translate into string, then return it
     return this.value;
  }
};

DOMElement.innerHTML = '<p>Hello</p>';
console.log(DOMElement.innerHTML);

If you want to print the given character without serializing, you can use textContent property.

like image 70
Suren Srapyan Avatar answered Oct 20 '22 23:10

Suren Srapyan


technically, I just made an assignment of one variable to another, both are strings. How come the values are different?

Because yes, innerHTML is special. It's not just a simple value property, it's an accessor property. When you assign to it, you're calling a function under-the-covers which parses the HTML you give it and creates the necessary DOM nodes and elements. When you read its value, you're calling another function under-the-covers which recurses through the DOM tree starting from the element on which you access it and builds an HTML string representing that DOM tree.

So while you assigned the character U+00A0 to it, that got turned into a DOM text node; and when you read from it, that DOM text node was rendered as a canonical (per that browser's rules) HTML string: &nbsp;.

You can see that innerHTML isn't just a simple value property by using the DOM to manipulate the element:

var target = document.getElementById("target");
target.innerHTML = "\u00A0";
console.log(target.innerHTML); // "&nbsp;"
target.appendChild(
  document.createElement("span")
);
console.log(target.innerHTML); // "&nbsp;<span></span>"
<div id="target"></div>

Note that it's possible to define accessor properties in JavaScript objects as well; it's not just the DOM that can have them:

var o = {
  get foo() {
    console.log("foo getter called");
    return this._foo;
  },
  set foo(value) {
    console.log("foo setter called");
    this._foo = value;
  }
};
console.log("foo: " + o.foo);
o.foo = 42;
console.log("foo: " + o.foo);

The fact that innerHTML is a property with a getter and setter is why code like this is poor practice:

for (var n = 0; n < something.length; ++n) {
    element.innerHTML += ", " + something[n];
}

That's constantly building and then destroying and rebuilding a DOM tree and making a bunch of hidden function calls. Instead, you'd build the string, then assign it.

like image 37
T.J. Crowder Avatar answered Oct 21 '22 00:10

T.J. Crowder