みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

for(var i=0;...) の i の値を保持するには?

-----
<ul>
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>

<script type='text/javascript'>
(function(){
var li = document.getElementsByTagName('li');

for(var i=0,max=li.length; i<max; i++){
li[i].onclick = function(){
alert(i);
};
}
})();
</script>
-----

上記スクリプトを実行すると、全てのli要素でクリックしたときに "3" がalertされます。
0,1,2 をそれぞれalertしたいのですが、どういった方法が考えられるでしょうか?

現在作成しているスクリプトでは、下記のようにidに値を保持しています。
もう少しスマートな方法がある気がするのですが…。

---
li[i].id = 'test' + i;
li[i].onclick = function(){
alert(this.id.replace(/^test(\d+)/, '$1'));
};
---

投稿日時 - 2009-06-22 20:09:52

QNo.5066147

困ってます

質問者が選んだベストアンサー

http://nanto.asablo.jp/blog/2005/12/04/165848
メモリーリークにみえて、そうでない・・・

>HTMLObjectに勝手にプロパティ
これはよくないことだじょ!そうおそわった。

ついでにいうと、いべんとをなんこもていぎするより
No3.みたいにおおもとにひとつでじゅうぶん。
innerHTMLで、ぜんたいをかきかえたとしてもOK!
と、TAGindex
http://www.tagindex.com/cgi-lib/q4bbs/patio.cgi
でおそわったじょ!

投稿日時 - 2009-06-23 06:31:01

お礼

参考URLの掲示ありがとうございます。

> メモリリーク
非常に難解で、まだ完全には理解できていないのですが、
「要素ノードとクロージャの間で循環的に参照(循環参照)してしまい、タブを閉じる際にIEがこれを解放しないためにリークする」
という理解でいいのでしょうか?

調査の結果、メモリリークの回避手段がいくつかあることがわかりました。

----
1. 関数をネストしない
HTML ページの DOM オブジェクトへの循環参照はメモリ リークが発生します。
http://support.microsoft.com/kb/830555/ja
IEのメモリリーク問題
http://p2b.jp/index.php?UID=1131336575

2. elementNode=null; で参照を外す
IE6のメモリリークを華麗に回避 - zorioの日記
http://d.hatena.ne.jp/zorio/20080609/1213028969

3. document一つをイベントリスナに登録し、クロージャを使わない (#3と同じ)
掲示板/JavaScript質問板/過去ログ/一覧/ onclik時に上部へ行く方法(特殊) - TAG index Webサイト
http://www.tagindex.com/kakolog/q4bbs/1301/1650.html

4. 使い終わったイベントハンドラを外す関数を定義する
クロージャとメモリリークについてのコピペ - JavaScriptとかPerlとかPHPとかさくらとか勉強する (>702の createLeakFreeClosure(closure) )
http://d.hatena.ne.jp/lesamoureuses/20080416/1208325055
----

「循環の仕組み」が不完全な理解なので、「1=理解、2=何となく理解、3,4=理解不能」と心許ないのが現状です。
今のところ、2. を応用して

----
(function(){
var li = document.getElementsByTagName('li');

for(var i=0,max=li.length; i<max; i++){
li[i].onclick = (function(i){
return function(){// functionを返す(クロージャ)
alert(i);
};
})(i);

li[i] = null;// 要素ノードの参照を外す
}

})();
----

で運用しようと考えていますが、これで問題ないでしょうか?
また、間違って理解しているところはないでしょうか?
(曖昧な理解なだけに心配です…。)

投稿日時 - 2009-06-23 19:33:51

このQ&Aは役に立ちましたか?

4人が「このQ&Aが役に立った」と投票しています

回答(11)

ANo.11

ごめん!かんちがい!
>li[i] = null; // 要素ノードの参照を外す
'}'のいち、みまちがえてました。

onclickをうわがきされると、うごかなくなるよ

投稿日時 - 2009-06-23 20:33:27

補足

まだ理解がおぼつかないですが、引き延ばしても仕方がないのでこれにて締め切りたいと思います。
そこでまたわからないことがあれば質問するかもしれません。その時はよろしくお願いします。

ありがとうございました。

投稿日時 - 2009-06-24 22:10:50

お礼

> onclickをうわがきされると、うごかなくなるよ
そうですね。
window.onload と同じく、同じ場所に上書きされると初めに登録したイベントが動かなくなります。
それは承知しているのですが、今回の場合同じ場所にonclickイベントを登録する意義が見あたらないのでいいかなと。

また、「コードはシンプルに」という点には拘ってまして、長いコードは出来るだけ避けたいという思いがあります。
でも、「イベントリスナを使う癖を付けておいた方が良いのでは」という気もしますし、
addEvent() 的な関数を使用して elementNode=null; がいいような気もします。

落としどころが難しく、悩みます…。

投稿日時 - 2009-06-23 21:40:30

ANo.10

たぶん、じぶんもよくりかいできていません。
ところで
>li[i] = null; // 要素ノードの参照を外す
は、なに?

そこまでよんで、なんでいべんといっぱいつけるかなぁ~^^;

投稿日時 - 2009-06-23 20:22:43

お礼

いろいろとアドバイス有り難うございます。

> そこまでよんで、なんでいべんといっぱいつけるかなぁ~^^;
- #3を理解できていない
- コードが複雑化するのは避けたい

理解できてない、のが大きいです。
よくわからずに使って弊害が出てしまうのは本末転倒なので…。(苦笑)

投稿日時 - 2009-06-23 21:26:56

ANo.9

そもそも、「保持する」必要ありますか?
まぁリソースは有効活用したほうがいいですが・・・

<script type='text/javascript'>
window.onload=function(){
var li = document.getElementsByTagName('li');
for(var i=0; i<li.length; i++){
li[i].onclick = function(){
var li = document.getElementsByTagName('li');
for(var i=0;i<li.length;i++){
if(li[i]==this) break;
}
alert(i);
};
}
};
</script>

<ul>
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>

投稿日時 - 2009-06-23 14:49:33

お礼

> そもそも、「保持する」必要ありますか?
実際には要素ノードリストを2組持ち、iの値で2つを関連づけています。
関連付けの仕組みを変えればiが不要となるはずですが、アルゴリズムを根本から直す必要がありそうです。
ちなみに、http://vird2002.s8.xrea.com/test/dl2tab.html が実際のスクリプトです。

「同じノードを探し出す」
初めはそのようにしていましたが、そもそも一度捕まえたノードは再利用した方が効率がいいんじゃないか、と思い質問に至りました。

掲示されたコードはアプローチの一つとして参考にさせていただきます。
ありがとうございます。

投稿日時 - 2009-06-23 21:23:10

ANo.7

前々からできるかどうか不安だった方法を、この際だから調べてみました。
邪道かも。決していい方法であるとは思っていません。

ie8,FireFox,Operaで確認。(うまくいくのか・・・うれしいような残念なような・・・)
HTMLObjectに勝手にプロパティ追加してます。

<ul>
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>

<script type='text/javascript'>
window.onload=function(){
var li = document.getElementsByTagName('li');

for(var i=0,max=li.length; i<max; i++){
li[i].i=i
li[i].onclick = function(){alert(this.i);
};
}
}
</script>

投稿日時 - 2009-06-22 21:24:55

お礼

> HTMLObjectに勝手にプロパティ追加してます。
こ、こんな方法があるとは…。
FirebugのDOMタブを見ると、「プロパティi」がちゃんと見えています。

IE7でも動きました。動いてしまうんですね…。

ちょっと怖くて使えそうにありませんが、興味深い現象でした。
ありがとうございます。

投稿日時 - 2009-06-22 21:58:47

ANo.6

window.onload = function (){
var li = document.getElementsByTagName('li');
for(var i=0,max=li.length; i<max; i++){
make_li_func(li[i],i);
}
}
function make_li_func(li,no){
var i=no+1;
var func=li;
func.onclick = function(){
alert(i);
}
}
で出来ました。
もしかして、原理はbabu_babueさんと同じかしら?

投稿日時 - 2009-06-22 21:19:12

お礼

回答ありがとうございます。

新しくユーザ定義関数を定義したのですね。
もうちょっと簡略化して、

---
var li = document.getElementsByTagName('li');

function test(node,i){
node.onclick = function(){
alert(i);
};
}
for(var i=0,max=li.length; i<max; i++){
test(li[i],i);
}
---

でも出来ました。

> もしかして、原理はbabu_babueさんと同じかしら?
「引数で値を渡す」という発想はbabu_babueさんと同じだと思います。
無名関数に引数を渡すためにクロージャを使っていたところが違う部分ですね。

投稿日時 - 2009-06-22 21:48:53

ANo.5

>どのような情報が足りないでしょうか?

くろ~じゃ・めもりーりーく・じょうけんつきこんぱいる

えらそうなことをいえるほど、おおくをしらないので・・・
ばぶ~!

投稿日時 - 2009-06-22 21:04:41

お礼

ありがとうございます。
キーワードだけでも参考になります。
質問当初の時点では「メモリーリーク」以外は知りませんでした。

なので、補足要求された情報は持ち合わせていなかったのだと思います。

投稿日時 - 2009-06-22 21:30:22

ANo.4

一例です。

<ul>
<li>test1</li>
<li>test2</li>
<li>test3</li>
</ul>

<script type='text/javascript'>
(function(){
var li = document.getElementsByTagName('li');

for(var i=0,max=li.length; i<max; i++){
li[i].onclick = (function(j){
return function(){alert(j);}
})(i);

}
})();

解説は以下をご参照ください。
http://www.atmarkit.co.jp/fdotnet/ajaxjs/ajaxjs03/ajaxjs03_04.html

投稿日時 - 2009-06-22 20:52:12

お礼

参考URLまでありがとうございます。
今読み終わったところです。

クロージャという仕組みを使っているのですね。
クロージャとは「関数を戻り値に持つ関数」で掲示されているコードでは
無名関数がクロージャとして機能している、と理解しました。
#1の方もクロージャを使われていたことから、クロージャを使う方法は広く使われているようだと想像できます。

理解が深まりました。ありがとうございます。

投稿日時 - 2009-06-22 21:27:13

ANo.3

//@cc_on
document./*@if(@_jscript)attachEvent('on'+ @else@*/addEventListener(/*@end@*/
 'click', function (evt) {
  var c = 0, e = evt./*@if(@_jscript) srcElement @else@*/ target /*@end@*/;

  if ('LI' == e.tagName) {
   while (e = e.previousSibling) if ('LI' == e.tagName) c++;
   alert(c);
  }
 }, false);
//ぜんかくくうはくは、てきとうになおしてね ばぶ~!
//これだといべんとを、はがしてないよね

投稿日時 - 2009-06-22 20:48:07

お礼

なるほど、これが「条件付きコンパイル」なのですか。難しいですね…。

ありがとうございます。

投稿日時 - 2009-06-22 21:10:59

ANo.2

(function(){
var li = document.getElementsByTagName('li'), o, c = 0;
while (o = li[c]) o.onclick = (function(i){ return function() {alert(i);};})(c++);
})();
でも、メモリーリーク。

投稿日時 - 2009-06-22 20:33:55

補足

ひょっとして、#2だけメモリリーク、でしょうか。

- #2でもメモリーリーク (#1,2の両方)
- こういうコートがあるけど、メモリーリーク (#2だけ)

見方によって、受け取り方が変わってしまいますね…。

投稿日時 - 2009-06-22 21:02:41

お礼

何度もありがとうございます。

#1,2ともにメモリリークするコードと指摘されているのでしょうか…。
何のために#1からこのように変化したのかが理解できていません。(汗)

投稿日時 - 2009-06-22 21:01:10

ANo.1

(function(){
var li = document.getElementsByTagName('li');

for(var i=0,max=li.length; i<max; i++){
li[i].onclick = (function(i){ return function() {alert(i);};})(i);
}
})();
とか

投稿日時 - 2009-06-22 20:30:35

お礼

なるほど!
functionオブジェクトを return したのですね。
私も

li[i].onclick = (function(i){ alert(i); })(i);

までは思いついたのですが、これでは即実行されてしまってどうしたものかと悩んでいました。

変数を定義しなくていいので非常にいい方法だとは思うのですが、コードが長いのが悩みどころです…。
もっとも、慣れれば気にならなくなるようにも思えるので、大きな問題ではないかもしれません。

ともあれ、解決の糸口が見えてほっとしました。
ありがとうございます。

> 補足要求
どのような情報が足りないでしょうか?

投稿日時 - 2009-06-22 20:51:20

あなたにオススメの質問