创建样式节点、添加innerHTML、添加到DOM、以及IE令人头疼的问题

发布于 2024-10-21 04:04:19 字数 2394 浏览 6 评论 0原文

我有一个由两部分组成的问题。

首先,场景:

由于我们在移动浏览器对 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);

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

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 元素。 (这两个标签都是只读的。)

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

更新 2:

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

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

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

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

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.

The sample 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:

  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:

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?

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

I have a sample of this code up on JSBIN:

http://jsbin.com/ucesi4/4

Anyone know what's going on with 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

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

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

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

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.

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

‘画卷フ 2024-10-28 04:04:19

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

查看此问题的答案:如何创建<样式>使用 JavaScript 标记

In IE you can use 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/

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

平定天下 2024-10-28 04:04:19

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

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

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.

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:

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

<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>
止于盛夏 2024-10-28 04:04:19

最佳实践是将 CSS 的所有

我建议采用一种简单得多的解决方案,而不是经历创建 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 中的“准备好”,或 addEventListener 'domcontextloaded',甚至 addEventListener 'load')。这保证在所有情况下都能正确运行。 但是在很多情况下它会闪烁(也许非常严重)。

我知道(但我希望其他人知道更多:-)完全避免闪烁的唯一方法是在 head 中放置一个 Javascript 片段,该片段会更改 window.location 但不执行任何操作别的。 (没有对 DOM 的引用,这通常通过代码中某处的“文档”一词显而易见。)如果 JS 可用,最终效果将是在向用户显示大部分内容之前立即重新加载不同的页面。初始页面可以包含您的警告,而新页面则包含没有警告的真实内容。

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

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.

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 much simpler 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:

<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, any attempt to change the DOM in any way before the page is initially displayed to the user is not guaranteed to work (in other words neither your way nor the example above will always work 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.

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. But it will flicker (perhaps quite badly) in many cases.

The only way I know (but I hope somebody else knows more:-) to avoid entirely the possibility of flicker is to put in the <head a 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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文