

HTML5とSpring Bootで勉強中です。

下記のような形で、画面上にコントロールソース側から渡したtestLabelが複数表示することを確認でき、  
islinkがtrueの場合に、画面上リンク可能な表示、押下するとjavascript側で`change()`が呼ばれることを確認できました。

ただ、このリストの何番目が押下されたかをjavascript側でどのように取得したらよいのでしょうか。  
また  
HTML側の `<a href="#" th:text="${test.testLabel}" onclick="change()"></a>` も  
期待した動きにはなっているのですが、通常このような記載はおかしいのでしょうか。  
＃onclickのあたりがどうなのだろうと思っています。

■javaコントロールソース側  
下記の情報をもつリストを返しています  
boolean islink  
String testLabel

■HTML側

[code]

    <div th:each="test, stat:${testlist}">
        <div th:if="${test.islink} == false">
            <div th:text="${test.testLabel}"></div>
        </div>
        <div th:if="${test.islink} == true">
            <a href="#" th:text="${test.testLabel}" onclick="change()"></a>
        </div>
    </div>
    
[/code]

■javaScript側

[code]

    function changeLanguage() {
    }
    
[/code]


CC BY-SA 3.0
2016-07-28T12:20:32.370
27919
2016-07-29T04:51:13.453
2016-07-28T12:36:21.583
8000
12842
question
javascript html
HTMLで表示しているデータ配列の番号をjavascript側で取得したい


質問にはjsライブラリについて言及がないので、ピュアjsで書きます。  
今回の問題を一般的にいうと、「ある要素について、その兄弟要素のなかでの順番を得たい」ということになります。であるならば、兄弟要素のリストをループで検索してしまうのが一番簡単でわかりやすいと思います。  
イベントハンドラの設定についてですが、個人的には`onclick`などの属性で設定するよりも、javascriptコードから設定することが、コード分離の観点から推奨されると考えています。またこうした子要素各々について処理を設定する場合、各子要素ではなく、これらを内包するコンテナ（親）要素にイベントハンドラを設定するのがスマートだと思います。  
ここでは、コンテナ要素に対してクリック時のイベントハンドラを設定し、子要素がクリックされたら、その要素の順番を取得するところまでをサンプルコードとして示します。

[code]

    // コンテナ要素にイベントハンドラを設定
    var container = document.getElementById('container');
    container.addEventListener('click', function (e) {
      // クリックされた子要素を特定
      var target = e.target || e.srcElement;
      while (target.parentElement !== container) {
        target = target.parentElement;
      }
      
      // 兄弟要素を検索して順番を取得
      var children = container.children;
      var index = -1;
      for (var i = 0; i < children.length; i++) {
        if (children[i] === target) {
          index = i;
        }
      }
      
      // index === -1のときはエラー
      if (index === -1) {
        console.error(target);
      } else {
        // 処理
        console.log('index = %d', index);
      }
    });
[/code]

[code]

    <div id="container" th:each="test, stat:${testlist}">
        <div th:if="${test.islink} == false">
            <div th:text="${test.testLabel}">text index=0</div>
        </div>
        <div th:if="${test.islink} == true">
            <a href="#" th:text="${test.testLabel}">link index=1</a>
        </div>
        <div th:if="${test.islink} == true">
            <a href="#" th:text="${test.testLabel}">link index=2</a>
        </div>
    </div>
[/code]

不明な点があればお手数ですがコメントをお願いします。

* * *

追記:

[code]

    // クリックされた子要素を特定
    var target = e.target || e.srcElement;
    while (target.parentElement !== container) {
      target = target.parentElement;
    }
    
[/code]

この部分について補足します。まず`var target = e.target ||
e.srcElement;`は、イベントハンドラに渡されるイベントオブジェクトから、実際にクリックイベントが発生したDOM要素を取得しています。`||`で
_srcElement_
を参照しているのは、互換性を考えて（古いブラウザでは`target`プロパティがなく、代わりに`srcElement`に同じものが入っている）のことですので、場合によっては不要かもしれません。  
また、`while()...`の部分ですが、先ほど取得した`target`、つまり実際にクリックイベントが発生した要素というのは、コンテナ要素の直下の要素（つまり子要素）ではなく、さらにその下に位置する孫要素かもしれません。たとえば`<a
href="#" th:text="${test.testLabel}">link
index=1</a>`とかです。こういう場合は兄弟要素のリストを検索しても該当しないので、親要素をたどっていって、コンテナ要素の子要素、つまり`<div
th:if="${test.islink} == true">`までたどり着くという処理になります。説明が下手ですがわからなければまた編集します。

[code]

    <!-- このコンテナ要素にイベントハンドラを設定 -->
    <div id="container" th:each="test, stat:${testlist}">
        <!-- これが子要素 -->
        <div th:if="${test.islink} == false">
            <!-- クリックイベントはこの要素（孫要素）で発生するかもしれない -->
            <!-- これをcontainer.childrenのなかから検索してもみつからない -->
            <div th:text="${test.testLabel}">text index=0</div>
        </div>
    
[/code]


CC BY-SA 3.0
2016-07-28T15:38:53.603
27924
2016-07-29T03:50:16.167
2016-07-29T03:50:16.167
27919
answer


## jQueryの例

欲しいインデックスは、  
「クリックされた要素の親要素が、（その親要素の）親要素の何番目に出現するか」  
です。

### クリックされた要素の取得

`onclick`属性内のコードでは、クリックイベントを`event`という変数で参照出来ます。  
ですので、ここで`change`関数を呼ぶ際に`event`を引数で渡します。

[code]

    onclick="change(event)"
    
[/code]

change関数ではこれを`ev`という名前で受ける事とします。

[code]

    function change(ev){
    
[/code]

クリックイベントには`target`というプロパティがあり、これがクリックされた要素です。  
change関数内では`ev.target`として参照出来る事になります。  
jQueryの要素オブジェクトにするには`$`に渡せばよいです。

[code]

    $(ev.target)
    
[/code]

### 親要素の取得

jQueryでは`parent()`メソッドが使えます。

[code]

    $(ev.target).parent()
    
[/code]

### インデックスの取得

jQueryでは`index()`メソッドが使えます。

[code]

    $(ev.target).parent().index()
    
[/code]

[code]

    function change(ev){
     var index = $(ev.target).parent().index();
     $("#show_index").text(index);
    }
[/code]

[code]

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div>
     <div>
      <div>Test Label 00</div>
     </div>
     <div>
      <a href="#" onclick="change(event)">Test Label 01</a>
     </div>
     <div>
      <div>Test Label 02</div>
     </div>
     <div>
      <a href="#" onclick="change(event)">Test Label 03</a>
     </div>
     <div>
      <a href="#" onclick="change(event)">Test Label 04</a>
     </div>
    </div>
    
    <p>インデックス: <span id="show_index"></span></p>
[/code]

* * *

以下、本題とずれます。

## サーバ側の見直し

### ネストを浅くする

ネストが深いと苦労します。  
表示したい要素を`<div th:if...>`
でくるまずに、表示したい要素のタグ自体に`th:if`を設定すれば、ネストが一つ減って`parent()`がいらなくなるのではないでしょうか。

### インデックスをサーバ側で生成しておく

おそらく、サーバサイドのコード、データ構造、DOM、JavaScriptが密結合になりすぎています。  
DOM構造とデータ構造を一致させるというのも一つの方法ですが、質問から察するにもう限界ではないでしょうか。

例えばThymeleaf(これを使っているのですよね？)には

> index プロパティ: 0始まりの現在の「繰り返しインデックス」  
>
> [繰り返しステータスの保持](http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf_ja.html#%E7%B9%B0%E3%82%8A%E8%BF%94%E3%81%97%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%81%AE%E4%BF%9D%E6%8C%81)

このような物がありますから、`onClick`での`change`関数呼び出し時にインデックスを引数で渡すようなHTMLを生成してしまえば、JavaScript側では何も考えなくてよいです。

[code]

    onclick="change(1)"
    
[/code]

DOM構造が変更されてもJavaScritの変更は必要なくなります。


CC BY-SA 3.0
2016-07-29T04:51:13.453
27932
2016-07-29T04:51:13.453
3054
27919
answer