Edited at

ChromeでCSSStyleSheet.insertRuleにハマった

More than 5 years have passed since last update.

Ajax通信中のインジケーター(グルグル回るやつ)に画像ではなくSVGを挿入してくれる NETEYE Activity Indicator を導入してちょっとハマったのでメモ。

他の画面では順調にグルグル回っていたのにある画面でChromeのみ以下のエラーが発生した。

Uncaught HierarchyRequestError: A Node was inserted somewhere it doesn't belong.

HierarchyRequestErrorが発生していてどうもDOM操作を誤っているような感じで、エラーメッセージも「ノードが属さない箇所に挿入しようとした」といった内容。

エラーが jquery.activity-indicator-1.0.0.js の150行目で発生しているので該当コードを見てみる。


jquery.activity-indicator-1.0.0.js

129:   // Check if Webkit CSS animations are available, as they work much better on the iPad

130: // than setTimeout() based animations.
131:
132: if (document.createElement('div').style.WebkitAnimationName !== undefined) {
133:
134: var animations = {};
135:
136: /**
137: * Animation strategy that uses dynamically created CSS animation rules.
138: */

139: animate = function(el, steps, duration) {
140: if (!animations[steps]) {
141: var name = 'spin' + steps;
142: var rule = '@-webkit-keyframes '+ name +' {';
143: for (var i=0; i < steps; i++) {
144: var p1 = Math.round(100000 / steps * i) / 1000;
145: var p2 = Math.round(100000 / steps * (i+1) - 1) / 1000;
146: var value = '% { -webkit-transform:rotate(' + Math.round(360 / steps * i) + 'deg); }\n';
147: rule += p1 + value + p2 + value;
148: }
149: rule += '100% { -webkit-transform:rotate(100deg); }\n}';
150: document.styleSheets[0].insertRule(rule);
151: animations[steps] = name;
152: }
153: el.css('-webkit-animation', animations[steps] + ' ' + duration +'s linear infinite');
154: };
155: }
156: else {

発生箇所はここ。


jquery.activity-indicator-1.0.0.js

150:               document.styleSheets[0].insertRule(rule);


DeveloperToolsでオブジェクトを見ても特におかしな所はなく困り果てていたが、document.styleSheets[0]が指すCSSファイルに正常/エラーとで差があった。

@import 'common.css';

@import 'hoge.css';

こんな具合に外部ファイルをインポートしているだけのCSSファイルをHTML上で最初に定義されている場合(= document.styleSheets[0])にHierarchyRequestErrorが発生しているようだったので、スタイルが定義されたCSSと読み込み順序を変えてみたらHierarchyRequestErrorは発生しなかった。

推測だがWebkitベースのブラウザではインポートをしているだけだとCSSOM上にノードがないとみなされている模様。そう考えると以下のメッセージもうなずける。

A Node was inserted somewhere it doesn't belong.

PullRequestしようと思ったけど、よくよく調べてみたら1年以上前に同じ問題でissueが上がっていてPullRequestが取り込まれていないっぽいだけだった。。

滅多に使わないだろうけどCSSStyleSheet.insertRuleを使用する場合は要注意だね。


2013/11/14追記

Google Chrome31にバージョンアップするとこんなエラーが出た。

Uncaught TypeError: Failed to execute 'insertRule' on 'CSSStyleSheet': 2 arguments required, but only 1 present. jquery.activity-indicator-1.0.0.min.js:10

insertRuleは使うなってことか。