質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.84%

  • JavaScript

    14831questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • jQuery

    6163questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

jQueryでクラス名を定数にすべきか?見やすい管理は?

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 240

kumakuma112

score 12

最近、変数名の命名規則を勉強しだして、セレクターやクラス名の管理方法に悩んでます。
Bootstrap4のソース真似てみたりいろいろ試してるのですが、何が一般的かイマイチピンときてないです。

 メンテしやすい&見やすいコードにするためには?

プラグインではないのですが、クラス名を毎回書くのがなんかしっくり来てないです。
いざ、定数のように扱おうと思うと、

$(〜〜)とaddClassでドットありなしが混在するため、
うまくまとめられないくてどうしたものかと考えてました。
メンテしやすい&見やすいソースコードになるのか、わからないです。

Selectorに$("#slider").find(".slider_control")とかをまとめて書くのがスッキリするのですが、
ドットやコントロール系を自動生成にしたいため、最初に書くとfindで見つからなくて怒られます。

"use strict";
/* ========================================================
// NOTE スライダー04
======================================================== */
$(function(){


  // NOTE セレクター
  // jQueryオブジェクトをキャッシュ
  var Selector = {
    TARGET             : $('#slider')
  }


  // NOTE クラス名
  var ClassName = {
    ACTIVE             : 'is-active',
    CONTAINER          : 'slider',
    VIEW               : 'slider_view',
    ITEM               : 'slider_view_item',
    NAV                : 'slider_nav',
    DOT                : 'slider_nav_item',
    CONTROL            : 'slider_control',
    BUTTON             : 'slider_control_item',
    PREV               : 'slider_control_item-prev',
    NEXT               : 'slider_control_item-next'
  }


  // NOTE テンプレート
  var Html = {
    DOT                 : '<button class="' + ClassName.DOT + '" type="button" data-role="none" role="button"></button>',
    PREV                : '<button class="' + ClassName.BUTTON + ClassName.PREV + '" type="button" data-role="none" role="button"></button>',
    NEXT                : '<button class="' + ClassName.BUTTON + ClassName.NEXT + '" type="button" data-role="none" role="button"></button>'
  }


  // NOTE 現在地を確認する変数
  var index = 1;


  // NOTE スライダーの数
  var count = $(ClassName.ITEM).length;


  // NOTE 設定
  function init(){

    // NOTE スライド要素にクラスを追加
    Selector.TARGET.children().each(function(){
      $(this).addClass(ClassName.ITEM);
    });

    // NOTE #sliderにクラスを追加と親タグを用意
    Selector.TARGET
      .addClass(ClassName.CONTAINER)
      .wrapInner('<div class="' + ClassName.VIEW + '">')
      .append('<div class="' + ClassName.NAV + '"/>')
      .append('<div class="' + ClassName.CONTROL + '"/>');

    // NOTE ドットナビを生成
    console.log(Selector.TARGET.find(ClassName.ITEM));
    Selector.TARGET.find(ClassName.ITEM).each(function(i){
      Selector.TARGET.find(ClassName.NAV).append(Html.DOT);
      $(ClassName.DOT).on("click",function(){
        view(i + 1);
      });
    });

    // NOTE 矢印コントローラーを生成
    Selector.TARGET
      .find(ClassName.CONTROL)
      .append(Html.PREV)
      .append(Html.NEXT);

    $(ClassName.BUTTON).each(function(i){
      $(ClassName.PREV).on("click",function(){
        view(i - 1);
      });
      $(ClassName.NEXT).on("click",function(){
        view(i + 1);
      });

    });

    // NOTE 最初に1回だけ実行
    view(index);

  }



  // NOTE 更新が必要なモジュールをアップデート
  function view(val){

    index = val;

    // スライド画像を更新
    console.log($(ClassName.ITEM));
    $(ClassName.ITEM)
      .hide()
      .each(function(i){
        if(index == i + 1){
          $(this).show();
          return false;
        }
      });

    // ドットのナビを更新
    $(ClassName.DOT)
      .removeClass(ClassName.ACTIVE)
      .each(function(i){
        if(index == i + 1){
          $(this).addClass(ClassName.ACTIVE);
          return false;
        }
      });

    // 矢印ボタンを更新
    if(index <= 1){
      $(ClassName.PREV).hide();
    } else {
      $(ClassName.PREV).show();
    }
    if(index >= count){
      $(ClassName.NEXT).hide();
    } else {
      $(ClassName.NEXT).show();
    }

  }




  //NOTE 実行
  init();


});
  <div id="slider">
    <a href="#"><img src="img/thumb-01.jpg" alt=""></a>
    <a href="#"><img src="img/thumb-02.jpg" alt=""></a>
    <a href="#"><img src="img/thumb-03.jpg" alt=""></a>
    <a href="#"><img src="img/thumb-04.jpg" alt=""></a>
    <a href="#"><img src="img/thumb-05.jpg" alt=""></a>
  </div>

 最終的なhtmlのイメージ

<div id="slider" class="slider">
  <div class="slider_view">
    <a class="slider_view_item" href="#"><img src="img/thumb-01.jpg" alt=""></a>
    <a class="slider_view_item" href="#"><img src="img/thumb-02.jpg" alt=""></a>
    <a class="slider_view_item" href="#"><img src="img/thumb-03.jpg" alt=""></a>
    <a class="slider_view_item" href="#"><img src="img/thumb-04.jpg" alt=""></a>
    <a class="slider_view_item" href="#"><img src="img/thumb-05.jpg" alt=""></a>
  </div>
  <div class="slider_nav">
    <button class="slider_nav_item dotButton" type="button" data-role="none" role="button"></button>
    <button class="slider_nav_item dotButton" type="button" data-role="none" role="button"></button>
    <button class="slider_nav_item dotButton" type="button" data-role="none" role="button"></button>
    <button class="slider_nav_item dotButton" type="button" data-role="none" role="button"></button>
    <button class="slider_nav_item dotButton" type="button" data-role="none" role="button"></button>
  </div>
  <div class="slider_control">
    <button class="slider_control_item slider_control_item-prev" type="button" data-role="none" role="button">prev</button>
    <button class="slider_control_item slider_control_item-next" type="button" data-role="none" role="button">next</button>
  </div>
</div>

 参考にしたもの

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+4

 注釈

質問内容が編集され当初の質問と前提が変わっているため、 以下、現在の質問内容に対する回答としては成立していません。

 編集前の質問内容に対する回答

クラス名を毎回書くのがなんかしっくり来てないです。
いざ、定数のように扱おうと思うと、

「クラス名を毎回書くのがなんかしっくり来てないです」までは良いのですが、
その後の方向性が少しズレているように感じます。

var TARGET = '#slider';

$(TARGET).hide();
$(TARGET).text('hello');
$(TARGET).show();

例えば、質問文のコードを簡略した上記のようなコードがあるとします。

このコードがやっていることは、

  1. domツリーからIDがsliderの要素を見つけてjQueryオブジェクトにして非表示にする
  2. domツリーからIDがsliderの要素を見つけてjQueryオブジェクトにしてテキストを変更する
  3. domツリーからIDがsliderの要素を見つけてjQueryオブジェクトにして表示する

といったことをしています。

毎回、「domツリーからIDがsliderの要素を見つけてjQueryオブジェクトにして」が繰り返されている訳ですが、これって非効率ですよね?

var $slider = $('#slider');
$slider.hide();
$slider.text('hello');
$slider.show();

なので、一般的にはこのようにjqueryオブジェクトに変換された要素をキャッシュさせて、それを使いまわすということをします。

そうすると、

  1. domツリーからIDがsliderの要素を見つけてjQueryオブジェクトにして変数$sliderに入れておく
  2. それを使いまわして非表示にする
  3. それを使いまわしてテキストを変更する
  4. それを使いまわして表示する

というかたちになり、パフォーマンスの向上に繋がります。

ということでまとめると、クラス名を定数化するというよりは、jQueryのセレクターを変数に入れて使いまわすということになります。

その上で、例えば、少し古い本ですが「シングルページWebアプリケーション ―Node.js、MongoDBを活用したJavaScript SPA」のコードをみてみると、以下のようにjQueryセクレターを管理していますので参考にしてみるのも良いかもしれません。

jqueryMap = {
      $slider : $slider,
      $head   : $slider.find( '.spa-chat-head' ),
      $toggle : $slider.find( '.spa-chat-head-toggle' ),
      $title  : $slider.find( '.spa-chat-head-title' ),
      $sizer  : $slider.find( '.spa-chat-sizer' ),
      $msgs   : $slider.find( '.spa-chat-msgs' ),
      $box    : $slider.find( '.spa-chat-box' ),
      $input  : $slider.find( '.spa-chat-input input[type=text]')
    };

https://github.com/mmikowski/spa/blob/master/listings/ch05_06/5.2/js/spa.chat.js#L94

 追記(変更された質問内容に合わせた回答)

コメント欄でLhankor_Mhyさんが書いておられたlazy gettersをベースに書いてみました。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/get#Smart_self-overwriting_lazy_getters

var obj = {
    get element() {
        console.log('1度だけ呼ばれる')
        delete this.element;
        return this.element = 'キャッシュ対象';
    }
}

console.log(obj.element) // 初回アクセス
console.log(obj.element) // 2回目以降はキャッシュが使われる

まずは、簡単なコードで動作確認。

    var selectors = {
        $slider: $('#slider'),
        get $items() {
            delete this.$items;
            this.$items = this.$slider.find('.item');
            return this.$items;
        }
    }

    selectors.$slider.children().each(function(){
        $(this).addClass('item');
    });

  selectors.$items.first().text('hello')

後からクラス名が追加される場合でも大丈夫。

→ https://codepen.io/anon/pen/pVQOXd

 追記2

var obj = {
    get element() {
        console.log('1度だけ呼ばれる')
        delete this.element;
        return this.element = 'キャッシュ対象';
    }
}

console.log(obj.element) // 初回アクセス
console.log(obj.element) // 2回目以降はキャッシュが使われる
obj.element = 'hoge';
console.log(obj.element) // 書き換わってしまう

これだと上書き出来てしまうので、上書き出来ない版も追加。

var obj = {
    get element() {
        console.log('1度だけ呼ばれる')
        delete this.element;

        Object.defineProperty(this, "element", { value: 'キャッシュ対象', writable: false, configurable: true})

        return this.element;
    }
}

console.log(obj.element) // 初回アクセス
console.log(obj.element) // 2回目以降はキャッシュが使われる
obj.element = 'hoge';
console.log(obj.element) // 書き換わらない

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/18 08:39

    勉強不足でした。確かにキャッシュしないと無駄な作業が増えますね。

    ソースコードに入れてなかったのですが、
    例えばスライダーでドットナビや矢印のコントローラーを動的に吐き出す場合に、
    DOM操作があとになるため最初に変数で親からfindって入れられないと思って、
    今回のような書き方しておりました。

    ```
    var ClassName = {
    VIEW_ITEM : 'slider_view_item'
    }

    //まだクラスが振られてないから見つからない
    var VIEW_ITEM = $("#slider").find(".slider_view_item");

    function init(){
    $("#slider").children().each(function(i){
    $(this).addClass(className.VIEW_ITEM);
    });
    }
    init();
    ```

    Bootstrap4ではセレクターとクラスをオブジェクト?で管理してるようです。
    ```
    $(Selector.FIXED_CONTENT).each((index, element) => {
    ```
    https://github.com/twbs/bootstrap/blob/v4-dev/js/src/modal.js#L464

    何がベストか混乱してます。
    そもそも何でもかんでもクラス名を変数にするのってWEBアプリやサイトのJS実装では、余りやらないのでしょうか?

    キャンセル

  • 2018/05/18 10:46

    複数回使うようなクラス名などの文字列を毎回手打ちしてるとタイプミスも起こりやすいですし、IDEの入力補完で楽も出来ないので、変数にいれて使い回すというのは良いことです。例えば、リンク先コードだと、$(this._element).hasClass(ClassName.FADE)のところなど。ただ、回答欄で書いた通り、同じセレクターに対するjqueryオブジェクトはキャッシュさせて使いまわした方が良いですね。

    キャンセル

  • 2018/05/18 11:26

    横からすみません。

    > DOM操作があとになるため最初に変数で親からfindって入れられない

    lazy getter と呼ばれる方法を使うのはいかがでしょうか。
    https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/get#Smart_self-overwriting_lazy_getters

    キャンセル

  • 2018/05/18 13:37

    変更された質問に合わせた回答も追記しました。Lhankor_Mhyさんがコメントされていたlazy getterを参考にさせて頂きました。情報ありがとうございます。

    キャンセル

  • 2018/05/18 13:54

    おお、こちらこそ取り上げていただきありがとうございます。
    実際には、関数を返すラッパ関数なんかを使って書くんでしょうね。
    デコレータが欲しくなりますね。

    キャンセル

  • 2018/05/18 18:40

    勉強になりました。ありがとうございます

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.84%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    14831questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • jQuery

    6163questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。