0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

aボタン設計

0
Posted at

ボタン

画面クリック=ボタン(1個)の例

Ex.js
document.addEventListener('DOMContentLoaded', () => {
    document.addEventListener('click',(e)=>{
      //画面にあるメニューを1個取ってくる
      const menu = document.querySelector('.menu');
      menu.classList.toggle('is-open');
    });
});
  • クリック発生=必ず“メニューボタンに対するクリック”
  • なら、何をする?(処理):今の状態を逆の状態にしたい
  • 「状態」→JSはこれを 見た目じゃなくて印(クラスetc)で判断する

種類は1つ、ボタンが複数ある

w.js
document.addEventListener('DOMContentLoaded', () => {
  document.addEventListener('click',(e)=>{
    //クリックしたのはmenu用のボタンや!
    const btn = e.target.closest('.menu-btn');
    const wrapper = btn.closest('li');//このボタンを含む li を取る
    const menu = wrapper.querySelector('.menu');//その li の中の menu を取る
    menu.classList.toggle('is-open');//menuを開閉
  });
});
  • クリック発生=必ず“どれかのメニューボタンに対するクリック”
  • 複数ある中の、どのボタンがクリックされたかを特定したい
  • そのボタンに“対応するメニュー”を見つける
  • 処理

どのボタンか識別

  • ボタンは複数ある
  • それぞれ「枠(li)」の中に入っている
  • クリックされた場所から上にたどっていけば、必ず“そのボタン本体”に行き着く
Ex.htm
<li>
  <a class="menu-btn">
    <svg>
      <path></path>
    </svg>
  </a>
  <div class="menu"></div>
</li>
  • ボタン本体:<a class="menu-btn">
  • その中に svg
  • その中に path
  • ユーザーは見た目として「ボタン」をクリックしている

実際に「クリックされる場所」はどこ?

  • 人間の感覚
    👉「ボタンをクリックした」
  • ブラウザの現実
    👉一番内側の要素がクリックされた
  • 例:アイコンの線をクリックした場合
<li>
  <a class="menu-btn">
    <svg>
      <path> ← 👈 実際にクリックされた場所(e.target)
    </svg>
  </a>
</li>
  • JS「path がクリックされたよ」
  • でも知りたいのは「どのボタンが押されたか」
  • だから 変換が必要:クリックされた場所から、親へ、親へと進む
  • これがclosest
a  (★ ボタン本体)
↑
svg
↑
path:e.target  (クリック地点)

たとえばA,B,Cの三つのボタンを識別するには

E.htm
<ul>
  <li>
    <a class="menu-btn" data-name="A">
      <span>A</span>
    </a>
  </li>

  <li>
    <a class="menu-btn" data-name="B">
      <span>B</span>
    </a>
  </li>

  <li>
    <a class="menu-btn" data-name="C">
      <span>C</span>
    </a>
  </li>
</ul>

クリック(例)

li
↑
a  (★ ボタン本体)
↑
span:e.target  (クリック地点)
E.js
const clicked = e.target;                // span
const btn = e.target.closest('.menu-btn'); // a[data-name="A"]
const whichBtn = btn.dataset.name;       // "A"

console.log(whichBtn); // A

対応するメニューとは

E.htm
<li>
  <button class="menu-btn">ボタン</button>
  <div class="menu">メニュー</div>
</li>
  • 「ボタンとメニューは、同じ li の中にある」
  • 👉 つまり対応関係は「距離」じゃなく「構造」で決める

クリック対象がメニューボタン以外にもある

  • メニュー以外のボタンとかaタグ以外のクリックとか
  • その中で先ほどのメニューボタンがクリックされたら
e.js
document.addEventListener('DOMContentLoaded', () => {
  document.addEventListener('click',(e)=>{
    const btn = e.target.closest('.menu-btn');
    if(!btn) return; //たどってもメニューボタンクラスもつaタグがない
    const wrapper = btn.closest('li');
    const menu = wrapper.querySelector('.menu');
    menu.classList.toggle('is-open');
  });
});

メニューが存在しないなど想定外の構造のボタン

e.js
document.addEventListener('DOMContentLoaded', () => {
      document.addEventListener('click',(e)=>{
        const btn = e.target.closest('.menu-btn');
        if(!btn) return; //クリックがメニューボタンへのものじゃない
        
        const wrapper = btn.closest('li');
        if(!wrapper)return; //このメニューボタンに li がない
        
        const menu = wrapper.querySelector('.menu');
        if(!menu)return; //li の中の menu がない
        menu.classList.toggle('is-open');
      });
  });

メニュー外クリックで閉じる

  • 今まで:クリックが
    • メニューボタンか? → btn
    • ちがうか? → return
  • つまり 「メニューボタンのクリック」だけは処理できている
  • 今から:メニューが開いている状態で、メニューでもボタンでもない場所をクリックしたら、メニューを閉じたい
    「閉じる条件」は“ボタンじゃない”だけでは足りない
r.js
document.addEventListener('DOMContentLoaded', () => {
      document.addEventListener('click',(e)=>{
        const btn = e.target.closest('.menu-btn');//a.menu-btn

        if(btn){
          const wrapper = btn.closest('li');//このボタンを含む li を取る
          if(!wrapper)return;
          const menu = wrapper.querySelector('.menu');//その li の中の menu を取る
          if(!menu)return;
          menu.classList.toggle('is-open');//menuを開閉
          return;
          }

        if(e.target.closest('.menu')){
          return;
        }
        //開いてるメニュー1個だけ
        const menu = document.querySelector('.menu.is-open');
        if(menu){//開いてるメニューがあれば閉じる処理
          menu.classList.remove('is-open');
        }
      });
  });

外クリックで開いてるメニュー閉じる→開いてるメニューはここでは1個のみが前提

menu A(open)  ← ← ← これ取る
menu B(closed)
menu C(closed)

画面がクリックされたとき、考えることは3つ

① それはメニューボタンか?
- YES → 処理(toggle)
- NO → 次へ

② それはメニューの中か?
- YES → 何もしない(操作中だから)
- NO → 次へ

③ それ以外か?
YES → メニューを閉じる

「メニューの中で起きたか?」をどう調べる?

  • 「クリックされた場所から上にたどって、.menu に行き着くか?」
例:メニュー内をクリックした場合
[li]
  [div.menu]
    [ul]
      [li]
        [a] ← クリック(e.target)
e.js
e.target.closest('.menu') //→ ある ✅
例:メニュー外をクリックした場合
[header]
  [div.container] ← クリック
e.js
e.target.closest('.menu') //→ null ❌

メニュー外クリックで閉じる(複数のメニューが開いてる場合)

menu A(open)
menu B(open)

さっきのコードのままだとAしか閉じない

ex.js
document.addEventListener('DOMContentLoaded', () => {
    document.addEventListener('click',(e)=>{
      const btn = e.target.closest('.menu-btn');//a.menu-btn

      if(btn){
        const wrapper = btn.closest('li');//このボタンを含む li を取る
        if(!wrapper)return;
        const menu = wrapper.querySelector('.menu');//その li の中の menu を取る
        if(!menu)return;
        menu.classList.toggle('is-open');//menuを開閉
        return;
        }

      if(e.target.closest('.menu')){
        return;
      }

      document.querySelectorAll('.is-open').forEach((menu)=>{
        menu.classList.remove('is-open');
      });
    });
});

メニューは同時に1つしか開けないようにする&ボタン再クリックで閉じる

Ex.js
document.addEventListener('DOMContentLoaded', () => {
      document.addEventListener('click',(e)=>{
        const btn = e.target.closest('.menu-btn');

        if(btn){
          const wrapper = btn.closest('li');
          if(!wrapper)return;

          const menu = wrapper.querySelector('.menu');
          if(!menu)return;

          // ✅ 排他トグル
          const isOpen = menu.classList.contains('is-open');//クリック時そいつは開いてた?閉じてた?

          document.querySelectorAll('.menu.is-open').forEach(el=>{
            el.classList.remove('is-open');//全部閉じる
          });

          if(!isOpen){//もともと閉じてたなら開く
            menu.classList.add('is-open');
          }

          return;
        }

        // メニュー内クリックは無視
        if(e.target.closest('.menu')){
          return;
        }

        // 外クリック → 全部閉じる
        document.querySelectorAll('.menu.is-open').forEach(menu=>{
          menu.classList.remove('is-open');
        });
      });
    });

排他トグルを使う

1個だけのメニューだったとき👇

Ex.js
menu.classList.toggle('is-open');//問題なし
  • 他のmenuが存在しない
  • 状態は1つだけ
  • 複数メニューになるとこうはうまくいかない
    →このセクションについてはこちらを参照:

data-action によるボタン分岐

E.htm
<a data-action="menu-toggle">メニューボタン</a>
<a data-action="modal-open" data-target="#">モーダルボタン</a>
ex.js
document.addEventListener('DOMContentLoaded', () => {
  document.addEventListener('click', (e) => {

    const btn = e.target.closest('[data-action]');
    const action = btn?.dataset.action;

    // ======================
    // ① メニュー用
    // ======================
    if (action === 'menu-toggle') {

      const wrapper = btn.closest('li');
      if (!wrapper) return;

      const menu = wrapper.querySelector('.menu');
      if (!menu) return;

      const isOpen = menu.classList.contains('is-open');
      document.querySelectorAll('.menu.is-open').forEach(el => {
        el.classList.remove('is-open');
      });
      if (!isOpen) {
        menu.classList.add('is-open');
      }

      return;
    }

    // ======================
    // ② モーダルとか(例)
    // ======================
    if (action === 'modal-open') {
    //そのボタンに書かれている“対象(target)”を使って、開くべきモーダルを探す
    // つまり、「○○っていうモーダルを開け」
      const target = document.querySelector(btn.dataset.target);
      if (target) {//そのモーダルがちゃんと見つかった?
        target.classList.add('is-open');//そのモーダルを表示状態にする
      }
      return;
    }

    // ======================
    // ③ メニュー内クリックは無視
    // ======================
    if (e.target.closest('.menu')) {
      return;
    }

    // ======================
    // ④ それ以外 → メニュー閉じる
    // ======================
    document.querySelectorAll('.menu.is-open').forEach(menu => {
      menu.classList.remove('is-open');
    });

  });
});

すべてのボタンにdata-actionをつける⇔data-actionがあるならボタン

Ex.js
//?:「btn があるなら進む」「無いならそこで止まる
//ボタンならそのactionを取得
const action = btn?.dataset.action;

ボタン

構造

構造で結びついてる

e.htm
<li>
  <a class="menu-btn"></a>
  <div class="menu"></div>
</li>

「このボタンのmenuは、同じliの中」

探し方

ex.js
//li
wrapper.querySelector('.menu')

モーダル

構造

構造が離れてる

EX.htm
<header>
  <a data-action="modal-open" data-target="#modalodal1">...</div>
</body>

👉 ボタンとモーダルは全然別の場所

探し方

  • data-target→ボタンにかいてあるセレクタを使って操作対象の要素を取得
  • 例:
- クリックされたボタンのdataset.target  → "#modal1"
- ボタンクリックで表示したいメニューは id='modal1'
↓
document.querySelector("#modal1")
:<div id="modal1">(メニュー本体) を取得

👉 名前で探す(ID)

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?