javascript 创建样式节点、添加innerHTML、添加到DOM、IE头痛

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5227088/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 16:27:17  来源:igfitidea点击:

Creating Style Node, adding innerHTML, add to DOM, and IE headaches

javascriptinternet-explorercreateelement

提问by DA.

I have a two part question.

我有一个两部分的问题。

First, the scenario:

一、场景:

Due to some bizarre issues we've run into in regards to mobile browser support for NOSCRIPT, I'm tasked with coming up with an alternative solution to 'detect' JS. The solution logic is to have two DIVs on the page. One is an error stating you do not have JS and his shown by default. If one has JS, we then want to add a new STYLE block to the HEAD that over-rides the previous CSS and hides the error and instead shows the content.

由于我们在移动浏览器对 NOSCRIPT 的支持方面遇到了一些奇怪的问题,我的任务是提出一种替代解决方案来“检测”JS。解决方案逻辑是在页面上有两个 DIV。一个是错误,说明您没有 JS 并且默认显示他。如果有 JS,那么我们想在 HEAD 中添加一个新的 STYLE 块,它会覆盖之前的 CSS 并隐藏错误并显示内容。

The sample HTML:

示例 HTML:

<div id="div1">div 1 (should be shown if JS enabled)</div>
<div id="div2">div 2 (should be hidden if JS enabled)</div>

This is the JS I started with:

这是我开始使用的 JS:

  var styleNode = document.createElement('style');
  styleNode.setAttribute("type", "text/css");
  styleNode.innerHTML = "#div1 {display: block;} #div2 {display: none;}";
  headTag.appendChild(styleNode);

But, I was having problems. Some googling resulting in this description of a security issue that IE can have if you try to insert innerHTML into a created element before placing it in the DOM:

但是,我遇到了问题。一些谷歌搜索导致了对 IE 可能存在的安全问题的描述,如果您在将其放入 DOM 之前尝试将 innerHTML 插入到创建的元素中:

http://karma.nucleuscms.org/item/101

http://karma.nucleuscms.org/item/101

So, I modified the script as such:

所以,我修改了脚本:

  var styleNode = document.createElement('style');
  styleNode.setAttribute("type", "text/css");
  var headTag = document.getElementsByTagName("head")[0];
  headTag.appendChild(styleNode);
  var aStyleTags = headTag.getElementsByTagName("style");
  var justAddedStyleTag = aStyleTags[aStyleTags.length-1];
  justAddedStyleTag.innerHTML = "#div1 {display: block;} #div2 {display: none;}";

question 1: is that a valid workaround for the IE issue? Is there a more efficient solution?

问题 1:这是 IE 问题的有效解决方法吗?有没有更有效的解决方案?

question 2: even with the adjustment, the script still does not work in IE. It works fine in Firefox, but in IE 7 I get an "unknown runtime error".

问题2:即使调整,脚本在IE中仍然无法运行。它在 Firefox 中运行良好,但在 IE 7 中出现“未知的运行时错误”。

I have a sample of this code up on JSBIN:

我在 JSBIN 上有此代码的示例:

http://jsbin.com/ucesi4/4

http://jsbin.com/ucesi4/4

Anyone know what's going on with IE?

有人知道IE是怎么回事吗?

UPDATE:

更新:

I stumbled upon this link via google. Note the last comment:

我通过谷歌偶然发现了这个链接。注意最后一条评论:

http://msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx

http://msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx

That said, you really should put all style rules in the HEAD for strict compliance with XHTML. Doing this can also be a little tricky because you cannot use innerHTML to inject into the HEAD or STYLE element directly. (Both of these tags are READ ONLY.)

也就是说,您真的应该将所有样式规则都放在 HEAD 中以严格遵守 XHTML。这样做也可能有点棘手,因为您不能使用 innerHTML 直接注入 HEAD 或 STYLE 元素。(这两个标签都是只读的。)

Eep! True? Is FireFox just being overly forgiving? Or is this just a very odd IE quirk?

哎呀!真的?FireFox 只是过于宽容了吗?或者这只是一个非常奇怪的 IE 怪癖?

UPDATE 2:

更新 2:

A bit more background on what we're trying to solve here. We're dealing with mobile devices and some of the antiquated devices a) don't support NOSCRIPT and b) have slow JS engines.

关于我们在这里尝试解决的问题的更多背景知识。我们正在处理移动设备和一些过时的设备 a) 不支持 NOSCRIPT 和 b) 具有缓慢的 JS 引擎。

Since they don't support NOSCRIPT, we are by default showing an error, then hiding it via JS if they have it, and presenting them with the proper content. Because of the slow JS engines on these, people see the 'flicker' of the DIV's showing/hiding. This was the proposed solution to handle that, as it would load the CSS before the DIVs were even rendered.

由于他们不支持 NOSCRIPT,我们默认显示错误,然后通过 JS 将其隐藏(如果有),并为他们提供正确的内容。由于这些上的 JS 引擎很慢,人们会看到 DIV 显示/隐藏的“闪烁”。这是处理该问题的建议解决方案,因为它甚至会在渲染 DIV 之前加载 CSS。

Since it appears to be invalid, the solution will be that on these old devices, we'll use this method (as it seems to work, even if not in IE) and then all other proper browsers will do as suggested...we'll just update the DISPLAY CSS property via inline JS after each DIV is loaded in the DOM.

由于它似乎无效,解决方案将是在这些旧设备上,我们将使用此方法(因为它似乎有效,即使不在 IE 中),然后所有其他适当的浏览器将按照建议进行...我们在 DOM 中加载每个 DIV 后,将通过内联 JS 更新 DISPLAY CSS 属性。

All that said, I'm still curious as to whether this issue is an IE bug, or if IE is actually adhering to the proper standards by making STYLE a read-only element.

尽管如此,我仍然很好奇这个问题是否是 IE 的错误,或者 IE 是否通过将 STYLE 设置为只读元素来真正遵守适当的标准。

回答by Arnaud Le Blanc

In IE you can use style.styleSheet.cssText:

在 IE 中,您可以使用style.styleSheet.cssText

var style = document.createElement('style');
style.type = 'text/css';

if (style.styleSheet) { // IE
    style.styleSheet.cssText = css;
} else {
    style.appendChild(document.createTextNode(css));
}

document.getElementsByTagName('head')[0].appendChild(style);

Try this here: http://jsfiddle.net/QqF77/

在这里试试这个:http: //jsfiddle.net/QqF77/

See the answer on this question: How to create a <style> tag with Javascript

请参阅此问题的答案:How to create a <style> tag with Javascript

回答by Martin Jespersen

Don't use innerHTML, use document.createTextNode()and your life will become infinitely better ;)

不要使用innerHTML,使用document.createTextNode(),你的生活会变得无限美好;)

var styleNode = document.createElement('style');
styleNode.setAttribute("type", "text/css");
var textNode = document.createTextNode("#div1 {display: block;} #div2 {display: none;}");
styleNode.appendChild(textNode);
headTag.appendChild(styleNode);

EDIT:

编辑:

Since this solution doesn't seem to work for you, I'd abandon it. Instead go for a solution where styles are already defined and where you just disabled/enable styles via javascript if available.

由于此解决方案似乎对您不起作用,因此我会放弃它。而是去寻找一个已经定义样式的解决方案,如果可用,您只需通过 javascript 禁用/启用样式。

You can do it this way:

你可以这样做:

<head>
<style>
.jsenabled #div2, #div1 { display: none;}
.jsenabled #div1, #div2 { display: block;}
</style>
<script>
//i know you don't use jQuery, but the solution should still be valid as a concept
//bind to DOM-ready, then set the class jsenabled on the body tag
$(function() {
 $(document.body).addClass('jsenabled');
});
</script>
</head>
<body>
<div id="div1">div 1 (should be shown if JS enabled)</div>
<div id="div2">div 2 (should be hidden if JS enabled)</div>
</body>

EDIT 2:

编辑2:

If it has to be done prior to DOM ready, you could do something kinda ugly like this:

如果必须在 DOM 准备好之前完成,你可以做一些像这样丑陋的事情:

<head>
<style>
#div2, .show { display: block;}
#div1, .hide { display: none;}
</style>
</head>
<body>
<div class="hide">
<script>document.write('</div><div id="div1">');</script>
   div 1 (should be shown if JS enabled)
</div>
<script>document.write('<div class="hide">');</script>
<div id="div2">div 2 (should be hidden if JS enabled)</div>
<script>document.write('</div>');</script>
</body>

Or to keep things simple, you could just do

或者为了让事情简单,你可以这样做

<head>
<script>document.write('<style>#div1 {display: block;} #div2 {display: none;}</style>');
</head>
<body>
<div id="div1">div 1 (should be shown if JS enabled)</div>
<div id="div2">div 2 (should be hidden if JS enabled)</div>
</body>

回答by Chuck Kollars

Best practice is to put all the <link for CSS into the <head, and most <script at tne very end of the <body (except short fragments of <script that should execute ASAP should go in the <head). Both the standard and browsers allow you to do silly things, but just because it's acceptable doesn't mean it's a good idea.

最佳实践是将 CSS 的所有 <link 放入 <head 中,并且将大多数 <script 放在 <body 的最末端(除了应该尽快执行的 <script 的短片段应该放在 <head 中”)。标准和浏览器都允许你做一些愚蠢的事情,但仅仅因为它是可以接受的并不意味着它是一个好主意。

Rather than go through all the rigamarole of creating a Style node and of manipulating the DOM (which just provide more opportunities for something to go wrong), I recommend a muchsimpler solution. Hard code as much as you can into your document (and style sheet?) in advance once (rather than creating things on the fly every time). Here's a rough example:

与其经历创建 Style 节点和操作 DOM 的所有繁琐程序(这只会为出现问题提供更多机会),我推荐一个简单的解决方案。提前一次将尽可能多的硬编码到您的文档(和样式表?)中(而不是每次都在飞行中创建东西)。这是一个粗略的例子:

<body ...
...
<div id="warning" style="display: block;">
JS is required for this site, but isn't available ...
</div>
<div id="message" style="display: none;">
JS is available
</div>
...

<script ...
var warningEl = document.getElementById('warning');
warningEl.style.display = 'none';
var messageEl = document.getElementById('message');
messageEl.style.display = 'block';
</script ...

This should work reasonably well most of the time (it will certainly work in more browsers than what you tried to do initially). However, anyattempt to change the DOM in anyway before the page is initially displayed to the user is notguaranteed to work (in other words neither your way nor the example abovewill alwayswork in all cases). The earlier you run your Javascript, the more likely DOM operations (including getElementById) may fail. And the later you run your Javascript, the more likely the user will notice a perceptible "flicker" of their display. So you're presented with a tradeoff choice between wide compatibility and noticeable flicker.

这在大多数情况下应该可以正常工作(它肯定会在比您最初尝试做的更多的浏览器中工作)。但是,在页面最初显示给用户之前以任何方式更改 DOM 的任何尝试都不能保证有效(换句话说,您的方式和上面的示例都不会在所有情况下始终有效)。越早运行 Javascript,DOM 操作(包括 getElementById)就越有可能失败。并且您运行 Javascript 的时间越晚,用户就越有可能注意到他们的显示有明显的“闪烁”。因此,您需要在广泛的兼容性和明显的闪烁之间做出权衡选择。

You can wait until the DOM is guaranteed to be fully ready before you run your Javascript ("ready" in jQuery, or addEventListener 'domcontextloaded', or even addEventListener 'load'). This is guaranteed to function correctly in all cases. Butit will flicker (perhaps quite badly) in many cases.

您可以等到 DOM 保证完全准备好后再运行您的 Javascript(jQuery 中的“准备好”,或 addEventListener 'domcontextloaded',甚至 addEventListener 'load')。这保证在所有情况下都能正常运行。在许多情况下它会闪烁(可能非常严重)。

The only way I know (but I hope somebody else knows more:-) to avoid entirely the possibility of flicker is to put in the <heada fragment of Javascript that changes window.location but does nothing else. (No references to the DOM, which are usually made obvious by the word "document" somewhere in the code.) The net effect if JS is available will be an immediate reload of a different page before much of anything is shown to the user. The initial page can contain your warning, and the new page the real stuff with no warning.

我知道的唯一方法(但我希望其他人知道更多:-) 完全避免闪烁的可能性是在 <head 中放入一个 Javascript 片段,它改变 window.location 但什么也不做。(没有对 DOM 的引用,这通常在代码中的某个地方用“文档”这个词来表示。)如果 JS 可用,最终效果将是在向用户显示大部分内容之前立即重新加载不同的页面。初始页面可以包含您的警告,而新页面是没有警告的真实内容。

Even this method has some disadvantages though: First, the double download takes extra bandwidth and delays user visibility a little. This won't matter on typical desktops. But on handhelds it may not be acceptable. And second, it will raise havoc with SEO. The second page -that's supposed to be invisible and only accessed from the first page- may show up independently in webdexes so users can easily access it directly (you may be able to "fix" this with clever use of "canonical" and/or "meta...robots"). And the SERP of the initial page may fall precipitously when its only content is the warning message.

即使这种方法也有一些缺点:首先,双重下载需要额外的带宽并稍微延迟用户的可见性。这在典型的桌面上无关紧要。但在手持设备上,这可能是不可接受的。其次,它将对 SEO 造成严重破坏。第二页 - 应该是不可见的,只能从第一页访问 - 可能会独立显示在 webdexes 中,因此用户可以轻松地直接访问它(您可以巧妙地使用“规范”和/或“元......机器人”)。并且初始页面的SERP可能会在其唯一内容是警告消息时急剧下降。