Hyperlink href incorrectly quoted in innerHTML?

喜你入骨 提交于 2020-06-29 03:47:35

问题


Take this very simple example HTML:

<html>
    <body>This is okay &amp; fine, but the encoding of <a href="http://example.com?a=1&b=2">this link</a> seems wrong.</body>
<html>

On examining document.body.innerHTML (e.g. in the browser's JS console, in JS itself, etc.), this is the value I see:

This is okay &amp; fine, but the encoding of <a href="http://example.com?a=1&amp;b=2">this link</a> seems wrong.

This behaviour is the same across browsers but I can't understand it, it seems wrong.

Specifically, the link in the orginal document is to http://example.com?a=1&b=2, whereas if the value of innerHTML is treated as HTML then it links to http://example.com?a=1&amp;b=2 which is NOT the same (e.g. If I created a new document, which actually had innerHTML as its inner HTML, and I clicked on the link then the browser would be sent to a materially different URL as far as I can see).

(EDIT #3: I'm wrong about the above. Firstly, yes, those two URLs are different; but secondly, the innerHTML which I thought was wrong is right, and it correctly represents the first URL, not the second! See the end of my own answer below.)

This is different from the issue discussed in question innerHTML gives me & as &amp; !. In my case (which is the opposite to the case in that question) the original HTML is correct and it looks to me as if it is the innerHTML which is wrong (i.e. because it is HTML which does not represent what the original HTML represented).

(EDIT #2: I was wrong about this, too: it's not really different. But I think it is not widely known that &amp; is the correct way to represent & inside an href, not just within body text. Once you realise that, then you can see that these are the same issue really.)

Can anyone explain this?

(EDIT #1+4: This only occurred to me a bit late, after writing my original question, but: "is &amp; actually correct within the href text, and & technically incorrect?" As I said when I first wrote those words, that "seems very unlikely! I've certainly never seen HTML written that way." But however 'unlikely', or not, that is the case, and is the main part of what I wasn't understanding!)

Also related and would be useful, can anyone explain how to cleanly get HTML which does correctly represent the target of document links? You definitely can't just un-encode all HTML character references within innerHTML, because (as shown in the example I've used, and also as discussed in innerHTML gives me & as &amp; !) the ones in the main run of text should be encoded, and just un-encoding everything would make these wrong.

I originally thought this was not a duplicate of innerHTML gives me & as &amp; ! (as discussed above; and in a way it still isn't, if it's agreed that it's not as obvious or widely known that the same issues apply inside href as in body text). It's still definitely not a duplicate of A href in innerHTML (which somehwat unclearly asks about how to set innerHTML using JS).


回答1:


Most browser tools don't show the actual HTML because it wouldn't be of much help:

  • HTML is often generated dynamically after page load with the help of CSS and JavaScript.
  • HTML is often broken and the browser needs to repair it in order to generate the memory representation needed for rendering and other stuff.

So the HTML you see is not the actual source but it's generated on the fly from the current status of the document, which of course includes all the fixed applied (in your case, the invalid HTML entities).

The following example hopefully illustrates all the combinations:

const section = document.querySelector("section");
const invalid = document.createElement("p");
invalid.innerHTML = '<a href="http://example.com/?a=1&b=2">Invalid HTML (dynamic)</a>';
const valid = document.createElement("p");
valid.innerHTML = '<a href="http://example.com/?a=1&amp;b=2">Valid HTML (dynamic)</a>';
section.appendChild(valid);
section.appendChild(invalid);
const paragraphs = document.querySelectorAll("p");
for (p of paragraphs) {
  console.log(p.innerHTML);
}
const links = document.querySelectorAll("a");
for (a of links) {
  console.log(a.getAttribute("href"));
}
<section>
  <p><a href="http://example.com/?a=1&b=2">Invalid HTML (static)</a></p>
  <p><a href="http://example.com/?a=1&amp;b=2">Valid HTML (static)</a></p>
<section>

Is &amp; actually correct within the href text, and & technically incorrect? It seems very unlikely! I've certainly never seen HTML written that way.

There's no such thing as "technically correct", let alone today when HTML is pretty well standardised. (Well, yes, there're two competing standards bodies and specs are continuously evolving, but the basics were set up long ago.)

The & symbol starts a character entity and &b is an invalid character entity. Period.

But it works! Doesn't that mean it's technically correct?

It works because browsers are explicitly designed to deal with completely broken markup, what's known as tag soup, because it was thought that it would ease usage:

<p><strong>Hello, World!</u>
<body><br itspartytime="yeah">
  <pink>It works!!!</red>

But HTML entities are just an encoding artefact. That doesn't mean that URLs are not allowed to contain literal ampersands, it just means that —when in HTML context— they need to be represented as &amp;. It's the same as when you type a backslash in a JavaScript string to escape some quotes: the backslash does not become part of your data.




回答2:


Having thought up a possible (but I thought 'unlikely') explanation - which I put in as an edit in the original question - I've realised that it is the answer:

  • Using & to represent & inside an href is technically incorrect, and &amp; is technically correct

I gathered this initially from this SO answer https://stackoverflow.com/a/16168585/795690, and I think it is relevant that (as it also says in that answer) the idea that &amp; is the correct way to represent & in an href is not as widely understood as the idea that &amp; is the correct way to represent & in body text.

Once you do understand this, it makes sense that what the browser is doing is right, and that the innerHTML value which comes back represents the link correctly.

EDIT:

@ÁlvaroGonzález gives a much longer answer, and it took me a while to see how everything he says applies, so I thought I'd try to explain what I didn't understand starting from where I started from, in case it helps someone else!

If you start with raw HTML with <a href="http://example.com/?a=1&b=1"> and then you inspect the DOM in the browser, or look at the value of the href attribute in JS then you see "http://example.com/?a=1&b=1" everywhere. So it looks as if nothing has changed, and nothing was wrong. What I didn't understand is that actually the browser has parsed a technically incorrect href (with invalid entities) to be able to display this to you! (Yes, LOTS of people use this 'broken' format!)

To see this first hand, load this longer HTML example into your browser:

<html>
    <body style="font-family: sans-serif">
        <p>Now & then <a href="http://example.com/?a=1&b=2">http://example.com/?a=1&b=2</a></p>
        <p>Now &amp; then <a href="http://example.com/?a=1&amp;b=2">http://example.com/?a=1&amp;b=2</a></p>
        <p>Now &amp;amp; then <a href="http://example.com/?a=1&amp;amp;b=2">http://example.com/?a=1&amp;amp;b=2</a></p>
    </body>
</html>

then in your javascript console try running this code taken from @ÁlvaroGonzález's answer:

const paragraphs = document.querySelectorAll("p");
for (p of paragraphs) {
  console.log(p.innerHTML);
}
const links = document.querySelectorAll("a");
for (a of links) {
  console.log(a.getAttribute("href"));
}

Also try clicking on the links to see where they go.

Once you've made sense of everything that you see there, it is no longer surprising how innerHTML works!



来源:https://stackoverflow.com/questions/62577621/hyperlink-href-incorrectly-quoted-in-innerhtml

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