创建样式节点,添加innerHTML,添加到DOM和IE头痛

时间:2011-03-08 00:46:20

标签: javascript internet-explorer createelement

我有两个问题。

首先,情景:

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

示例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>

这是我开始使用的JS:

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

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

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

所以,我修改了脚本:

  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;}";

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

问题2 :即使进行了调整,脚本仍然无法在IE中运行。它在Firefox中工作正常,但在IE 7中我得到了“未知的运行时错误”。

我在JSBIN上有这个代码的示例:

http://jsbin.com/ucesi4/4

任何人都知道IE正在发生什么事吗?

更新:

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

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

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

EEP!真正? FireFox是否过于原谅?或者这只是一个非常奇怪的IE怪癖?

更新2:

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

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

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

所有这一切,我仍然很好奇这个问题是IE漏洞,还是IE实际上是通过使STYLE成为只读元素来坚持正确的标准。

3 个答案:

答案 0 :(得分:6)

在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);

在此处试试:http://jsfiddle.net/QqF77/

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

答案 1 :(得分:1)

不要使用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);

编辑:

由于此解决方案似乎不适合您,我会放弃它。而是寻找已定义样式的解决方案,以及通过javascript禁用/启用样式的解决方案。

你可以这样做:

<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>

编辑2:

如果必须在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>

或者为了简单起见,你可以做到

<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>

答案 2 :(得分:0)

最佳做法是将所有&lt; link for CSS放入&lt; head,并将大多数&lt;脚本放在&lt; body的末尾(除了应该执行ASAP的脚本的短片段应该进入) &lt; head)。标准和浏览器都允许你做愚蠢的事情,但仅仅因为它是可接受的并不意味着它是一个好主意。

我建议使用很多更简单的解决方案,而不是经历创建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 ...

这在大多数情况下应该运行得相当好(它肯定会在比你最初尝试的浏览器更多的浏览器中工作)。 然而任何尝试在页面最初显示给用户之前以任何方式更改DOM 保证工作(换句话说,无论你的方式还是上面的例子 总是总是在所有情况下工作)。运行Javascript越早,DOM操作(包括getElementById)就越有可能失败。然后你运行你的Javascript,用户越有可能注意到他们的显示器可察觉的“闪烁”。因此,您可以在广泛的兼容性和明显的闪烁之间进行权衡选择。

您可以等到DOM保证完全就绪,然后再运行Javascript(jQuery中的“ready”,或者addEventListener'domcontextloaded',甚至addEventListener'load')。这保证在所有情况下都能正常工作。 在许多情况下它会闪烁(也许非常糟糕)。

我知道的唯一方法(但我希望其他人知道更多:-)完全避免闪烁的可能性是将放入&lt; head 一个更改window.location的Javascript片段但没有别的。 (没有对DOM的引用,这通常在代码中的某个地方通过单词“document”显而易见。)如果JS可用,那么净效果将立即重新加载不同的页面,然后向用户显示大部分内容。初始页面可以包含您的警告,新页面可以包含您的警告。

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