JavaScript クラス名で取得した要素をループ中にremoveするとおかしくなる

備忘録、調査、試したこと、解決策をメモ。(スマートな正解かどうかは不明)

クラス名で取得した要素のclassをループ中にremoveするとおかしくなる

HTMLでこのように記述して、

  <div class="enemy monster slime">スライムA</div>
  <div class="enemy monster slime">スライムB</div>
  <div class="enemy monster goblin">ゴブリン</div>
  <div class="enemy monster ogre">オーガ</div>

JavaScriptはこのように記述。

  var enemys = document.getElementsByClassName('enemy');
  for(var i=0; i<enemys.length; i++) {
      console.log(enemys[i]);
  }
  for(var i=0; i<enemys.length; i++) {
    console.log('I am ' + enemys[i].textContent);
    enemys[i].classList.remove('enemy');
    console.log('who am I? I am ' + enemys[i].textContent);
  }

1行目でclass名enemyで要素を取ってきている。HTMLには全てのdivタグにclass=enemyを入れてるので、スライムA~オーガまで全部とってくることになる。

2行目からforループでまわしてenemysをコンソールに表示してる。

次のforでenemysのHTMLCollectionに対し1つ1つ取り出して、次の3行を行っている。

① I am textContentで、名前を表示して
② classList.removeで、enemyというクラス名を削除
③ 再び名前を表示

これをやると、コンソールには以下のように表示される。

I am スライムA
who am I? I am スライムB
I am ゴブリン
who am I? I am オーガ

クラス名enemyで取得した要素のクラス名enemyをremoveすると、その要素自体を削除してしまうことになるってことだと思う。

こうなっちゃってる。

2つの問題。

・クラス名で取得した要素のクラス名を消すと要素ごときえる
・配列をforループ中に消すとおかしくなる

次にやったこと↓

クラス名で取得した要素を、forループで後ろから消すとどうなるか?

HTMLはこれで、さっきと同じだけどクラス名を変えてる。

  <div class="nakama human yuusya">勇者</div>
  <div class="nakama human magicuser">魔法使い</div>
  <div class="nakama human sensi">戦士</div>
  <div class="nakama monster goblin">ゴブリン</div>

JavaScriptはこう

  var nakamas = document.getElementsByClassName('nakama');
  for(var i=0; i<nakamas.length; i++) {
      console.log(nakamas[i]);
  }
  for(var i=nakamas.length-1; i>=0; i--) {
    console.log('I am ' + nakamas[i].textContent);
    nakamas[i].classList.remove('nakama');
    //console.log('who am I? I am ' + nakamas[i].textContent); //このnakama[i]には既にアクセスできなくなる
  }
  console.log('nakamas.length : ' + nakamas.length);

JavaScriptでは1行目でクラス名nakamaで要素たちを取得してきている。

2つ目のforループの中で、nakamasを一番後ろの要素から調べていっている。

後ろから削除することで要素が前に来ることが無いのでズレがなくなる。

けど、クラス名nakamaで取得した要素のクラス名nakamaをremoveしちゃうと、やはり要素は消えてしまう。

クラス名をremoveして(要素を消してしまった)後にnakamas[i]にアクセスすると、undefinedになっちゃうのでコメントアウトしている。

forループを抜けてから、nakamasの長さをコンソールに表示してみると

nakamas.length : 0

こうなる。

問題はやっぱりこれ。

・クラス名で取得した要素のクラス名を消すと要素ごときえる

要素ごと消したいならこれでもいいけど、そうじゃない場合は不都合になっちゃう。

次で調べたのは

取ってきたクラス名とは違うクラスを削除するとどうなるか?

これは挙動を調べてみただけ。

HTMLはこれ。npcクラス名で要素を取得したい。

  <div class="npc human townpeople">町人</div>
  <div class="npc human soldier">兵士</div>
  <div class="npc human king">王様</div>
  <div class="npc human inn">宿屋</div>
  var npcs = document.getElementsByClassName('npc');
  for(var i=0; i<npcs.length; i++) {
      console.log(npcs[i]);
  }
  for(var i=0; i<npcs.length; i++) {
    console.log('I am ' + npcs[i].textContent);
    npcs[i].classList.remove('human');
    console.log('who am I? I am ' + npcs[i].textContent);
  }
  console.log('npcs.length : ' + npcs.length);

JavaScriptの1行目ではnpcというクラス名で要素を取得してきている。

2つ目のforループの中ではnpcというクラス名じゃなくて、humanというクラス名をremoveしてみた。

コンソールには以下のように表示された。

I am 町人
who am I? I am 町人
I am 兵士
who am I? I am 兵士
I am 王様
who am I? I am 王様
I am 宿屋
who am I? I am 宿屋
npcs.length : 4

取ってきたクラス名(npc)とは違うクラス名(human)をremoveした時は、要素自体は削除されなかったし、ズレることもなかった。

I am 町人は、削除後のWho am Iでも町人になっている。

最後にnpcsのlengthを表示すると4になっていて要素もちゃんと残っている。

クラス名で取得した要素を、配列に変換してからやるとうまくいった

もう事例が思いつかない。クラス名magicで取ってくる予定。あとidを付与しておく。

  <div id="fff" class="magic fire">ファイヤ</div>
  <div id="hhh" class="magic ice">ヒャダイン</div>
  <div id="ttt" class="magic thunder">サンダー</div>
  <div id="rrr" class="magic recov">ホイミ</div>

JavaScriptは

  var magics = document.getElementsByClassName('magic');
  var magicArray = Array.prototype.slice.call(magics);
  for (var i=0; i<magicArray.length; i++) {
    let magicElm = document.getElementById(magicArray[i].id);
    console.log('I am ' + magicElm.textContent);
    magicElm.classList.remove('magic');
    console.log('who am I? I am ' + magicElm.textContent);
  }
  console.log('magics.length : ' + magics.length);
  console.log(magicArray); //退避用につくったmagicArray配列はのこる、要素に対して何かするときはここを触ればよい

1行目でクラス名magicで要素を取得してきた。
2行目はそれをもとにArray.prototype.slice.call()メソッドを使って、HTMLCollectionから配列にしてあげた。

forループでは配列magicArrayを全てループでまわしている。

4行目は配列の中の要素のidで改めて要素を取得して、それをmagicElmに入れている。

コンソールに表示した内容は以下のようになった。

I am ファイヤ
who am I? I am ファイヤ
I am ヒャダイン
who am I? I am ヒャダイン
I am サンダー
who am I? I am サンダー
I am ホイミ
who am I? I am ホイミ
magics.length : 0
Array(4) div#fff.fire, div#hhh.ice, div#ttt.thunder, div#rrr.recov

6行目で、magicElmはidをもとに生成した要素でmagicクラスを削除した。
magicElmはremove(‘magic’)をする前後で変わってないし要素は残っている。

ループを抜けた後のmagics.lengthは0なので、やっぱりmagicクラスでとってきた要素は消えてる。
magicArrayの配列はlengthが4で、中身も入ってる。

ブラウザの要素の検証で、HTMLを見ると以下のように

<div id="fff" class="fire">ファイヤ</div>
<div id="hhh" class="icd">ヒャダイン</div>
<div id="ttt" class="thunder">サンダー</div>
<div id="rrr" class="recov">ホイミ</div>

magicクラスが消えてたので。このやり方で自分がやりたいことが実現できた。

CodePen。コンソール部分が出ないので、自分のCodePenに張り付けてコンソールを表示するか、HTMLファイルを自作して試してみてください。

See the Pen
live-youso-remove-test1
by pghappy (@pghappy)
on CodePen.

タイトルとURLをコピーしました