回答編集履歴

6

テキスト追加

2021/10/06 17:21

投稿

退会済みユーザー
test CHANGED
@@ -281,3 +281,317 @@
281
281
 
282
282
 
283
283
  とします。➡ [サンプル3](https://codepen.io/i8086x/pen/powBqoe?editors=1010)
284
+
285
+
286
+
287
+
288
+
289
+ ## コメントに対する回答
290
+
291
+
292
+
293
+ コメントへ回答するために、以下のサンプルで説明します。(下記のHTMLを丸ごとコピペしてHTMLファイルを作り、ブラウザで表示させてご確認ください)
294
+
295
+
296
+
297
+ ```html
298
+
299
+ <!DOCTYPE html>
300
+
301
+ <html lang="ja">
302
+
303
+ <head>
304
+
305
+ <meta charset="UTF-8">
306
+
307
+ <title>362305@teratail</title>
308
+
309
+ <style>
310
+
311
+ .root { width: 510px; }
312
+
313
+ body > .container { padding-left: 10px; }
314
+
315
+ body > .container > .container { padding-left: 30px; }
316
+
317
+ body > .container > .container > .container { padding-left: 50px; }
318
+
319
+ body > .container > .container > .container > .container { padding-left: 70px; }
320
+
321
+
322
+
323
+ .header__nav-btn, .cover-darken { margin-bottom: 15px; height: 36px; display: flex; align-items: center; padding-left: 5px; font-size: 16pt; cursor: pointer; color: white; }
324
+
325
+ .header__nav-btn { background-color: red; }
326
+
327
+ .cover-darken { background-color: blue; }
328
+
329
+
330
+
331
+ .result {
332
+
333
+ display: flex;
334
+
335
+ align-items: center;
336
+
337
+ justify-content: center;
338
+
339
+ width: 520px;
340
+
341
+ height: 60px;
342
+
343
+ text-align: center;
344
+
345
+ font-size: 20pt;
346
+
347
+ background-color: yellow;
348
+
349
+ }
350
+
351
+
352
+
353
+ .js-open:after {
354
+
355
+ content: 'is-opened: No'
356
+
357
+ }
358
+
359
+
360
+
361
+ .js-open.is-opened:after {
362
+
363
+ content: 'is-opened: Yes'
364
+
365
+ }
366
+
367
+
368
+
369
+ </style>
370
+
371
+ <script>
372
+
373
+ document.addEventListener('DOMContentLoaded', function () {
374
+
375
+
376
+
377
+ function addClassToggleOnTriggers(toggledItemSelector, toggledClass, triggerSelector) {
378
+
379
+
380
+
381
+ // CSSクラスをトグルする対象要素を特定する。(与えられたセレクタで複数要素が該当しても先頭要素のみを対象にする。これは既存を踏襲)
382
+
383
+ const toggledItem = document.querySelector(toggledItemSelector);
384
+
385
+ if (!toggledItem) return;
386
+
387
+
388
+
389
+ // CSSクラスをトグルする関数
390
+
391
+ const handleToggle = () => {
392
+
393
+ toggledItem.classList.toggle(toggledClass);
394
+
395
+ }
396
+
397
+
398
+
399
+ // トグル実行のトリガーとなる要素に、トグルを実行する関数をaddEventListenerする。
400
+
401
+ document.querySelectorAll(triggerSelector).forEach(triggerElm => {
402
+
403
+ console.log(triggerElm.textContent);
404
+
405
+ triggerElm.addEventListener('click', handleToggle);
406
+
407
+ });
408
+
409
+ }
410
+
411
+
412
+
413
+ addClassToggleOnTriggers('.js-open', 'is-opened', '.header__nav-btn, .cover-darken');
414
+
415
+ })
416
+
417
+ </script>
418
+
419
+ </head>
420
+
421
+ <body>
422
+
423
+
424
+
425
+ <div class="container root">
426
+
427
+ <div class="cover-darken">1</div>
428
+
429
+ <div class="header__nav-btn">2</div>
430
+
431
+ <div class="container">
432
+
433
+ <div class="header__nav-btn">4</div>
434
+
435
+ <div class="header__nav-btn">5</div>
436
+
437
+ <div class="container">
438
+
439
+ <div class="cover-darken">9</div>
440
+
441
+ <div class="header__nav-btn">10</div>
442
+
443
+ <div class="container">
444
+
445
+ <div class="cover-darken">12</div>
446
+
447
+ </div>
448
+
449
+ </div>
450
+
451
+ <div class="cover-darken">6</div>
452
+
453
+ </div>
454
+
455
+ <div class="container">
456
+
457
+ <div class="container">
458
+
459
+ <div class="cover-darken">11</div>
460
+
461
+ </div>
462
+
463
+ <div class="header__nav-btn">7</div>
464
+
465
+ <div class="header__nav-btn">8</div>
466
+
467
+ </div>
468
+
469
+ <div class="cover-darken">3</div>
470
+
471
+ </div>
472
+
473
+
474
+
475
+ <div class="result">
476
+
477
+ <div class="js-open is-opened"></div>
478
+
479
+ </div>
480
+
481
+
482
+
483
+ </body>
484
+
485
+ </html>
486
+
487
+
488
+
489
+
490
+
491
+ ```
492
+
493
+
494
+
495
+ 上記のHTMLをブラウザで表示させると以下のようになります。
496
+
497
+
498
+
499
+ ![イメージ説明](96ba4f13107d0f6b02857ee3fc1ba593.png)
500
+
501
+
502
+
503
+ - 赤い矩形は `.header__nav-btn` です。青い矩形は `.cover-darken` です。
504
+
505
+ - 赤または青の矩形をクリックすると、黄色の矩形の中のテキストが、**is-opened: Yes** と **is-opened: No** とを切り替えます。
506
+
507
+ - 赤と青の矩形の左端に、白い数字が書かれています。数字の規則は以下です。
508
+
509
+ ・ DOMツリーの中で深い場所にあるものほど、数字は大きくなります。
510
+
511
+     ・ 同じ深さにある矩形は、連続した数字がふられます。
512
+
513
+
514
+
515
+ Javascriptの中に
516
+
517
+ ```javascript
518
+
519
+ // トグル実行のトリガーとなる要素に、トグルを実行する関数をaddEventListenerする。
520
+
521
+ document.querySelectorAll(triggerSelector).forEach(triggerElm => {
522
+
523
+ console.log(triggerElm.textContent);
524
+
525
+ triggerElm.addEventListener('click', handleToggle);
526
+
527
+ });
528
+
529
+ ```
530
+
531
+ というコードがありますが、これの `triggerSelector` に渡されるセレクターの文字列は
532
+
533
+ `'.header__nav-btn, .cover-darken'`
534
+
535
+ です。これは `.header__nav-btn` と `.cover-darken`をカンマ区切りで並べた[セレクタリスト](https://developer.mozilla.org/ja/docs/Web/CSS/Selector_list)です。
536
+
537
+
538
+
539
+
540
+
541
+ また、`forEach` に渡している関数の中で
542
+
543
+ ```javascript
544
+
545
+ console.log(triggerElm.textContent);
546
+
547
+ ```
548
+
549
+ で、`triggerElm` のテキスト内容を出力させています。以下はこれによって出力されたログです。
550
+
551
+
552
+
553
+ ![イメージ説明](a3421f635db3f66cb92fb19765d6456c.png)
554
+
555
+
556
+
557
+ これは、`querySelectorAll('.header__nav-btn, .cover-darken')` で取得される要素のリストに、どのような順番で要素が並んでいるかを表しています。
558
+
559
+
560
+
561
+ この取り出される順番について、querySelectorAllの仕様の確認をすると、W3Cの[Selectors API](https://www.w3.org/TR/selectors-api/) のドキュメントに以下の記述があります。
562
+
563
+
564
+
565
+ > 6.2. Finding Elements
566
+
567
+ ・・・
568
+
569
+ The querySelectorAll() methods on the Document, DocumentFragment, and Element interfaces must return a NodeList containing all of the matching Element nodes within the subtrees of the context node, in document order. If there are no matching nodes, the method must return an empty NodeList.
570
+
571
+
572
+
573
+ とあり、この中の "in document order" のリンク先に、以下の一文があります。
574
+
575
+
576
+
577
+ > The term document order means a depth-first pre-order traversal of the DOM tree or subtree in question.
578
+
579
+
580
+
581
+ この中にある、 depth-first で pre-order なDOMツリーの巡回をした結果が、先ほどの以下です。
582
+
583
+
584
+
585
+ ![イメージ説明](7afd8a56dd2c02b5f65aa5bf8087c41d.png)
586
+
587
+
588
+
589
+ depth-first で pre-order なツリーの巡回というのは、絵で説明すると、以下のような順序でノードをたどる辿り方です。
590
+
591
+
592
+
593
+ ![イメージ説明](cb43fb47027eddaa7032d9daa0b5e6c8.png)
594
+
595
+
596
+
597
+ なお、depth-first (深さ優先)に対照的な辿り方は、breadth-first (幅優先)で、pre-order に対照される辿り方には in-order や post-order があります。ご興味あれば調べてみるとよいかもしれません。

5

テキスト修正

2021/10/06 17:21

投稿

退会済みユーザー
test CHANGED
@@ -108,9 +108,7 @@
108
108
 
109
109
 
110
110
 
111
- jQueryもどきを自作してみる、っていうのはどうでしょう?
111
+ jQueryもどきを自作してみる、っていうのはどうでしょう? たとえば
112
-
113
-
114
112
 
115
113
  ```javascript
116
114
 

4

テキスト追加

2021/10/03 01:15

投稿

退会済みユーザー
test CHANGED
@@ -104,7 +104,7 @@
104
104
 
105
105
 
106
106
 
107
- ### 別案
107
+ ### 別案1
108
108
 
109
109
 
110
110
 
@@ -199,3 +199,87 @@
199
199
 
200
200
 
201
201
  ですが、こんな怪しげなまがいものを自作して、プチ[John Resig](https://ja.wikipedia.org/wiki/%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%BB%E3%83%AC%E3%82%B7%E3%82%B0)になった自己満足に浸るヒマがあったら、[本物](https://jquery.com/)を使えるようになったほうが断然いいです。ということで、別案を提示するつもりが、質問の本題からはそれて、「jQueryを覚えてみては?」というのが結論になってしまいました。申し訳ありません。
202
+
203
+
204
+
205
+
206
+
207
+ ### 別案2
208
+
209
+
210
+
211
+ この回答のはじめに挙げた
212
+
213
+
214
+
215
+ > `menu_open()`関数の引数を以下のように拡張します。
216
+
217
+ >
218
+
219
+ > ・第1引数: `toggledItemSelector` --- 第2引数で与えるCSSクラスをトグルしたい要素のセレクタ
220
+
221
+ > ・第2引数: `toggledClass` --- 第1引数のセレクタに該当する要素に対してトグルしたいCSSクラス
222
+
223
+ > ・残余引数: `...triggerSelectors` --- トグルを行うトリガーとなる(クリッカブルな)要素のセレクタ群
224
+
225
+
226
+
227
+ という提案を修正した案を挙げておきます。どこを修正するかというと、
228
+
229
+
230
+
231
+ > ・残余引数: `...triggerSelectors` --- トグルを行うトリガーとなる(クリッカブルな)要素のセレクタ群
232
+
233
+
234
+
235
+ の部分です。ここを残余引数で渡すのではなく、ひとつのセレクタの文字列で渡すことができます。というのは、セレクタの文法で、複数のセレクタのOR結合は、カンマ(`,`)で区切れば実現できるからです。
236
+
237
+
238
+
239
+ また先に書いたように、関数名を `addClassToggleOnTriggers` に修正します。以下はこの修正によるコードです。
240
+
241
+ ```javascript
242
+
243
+ function addClassToggleOnTriggers(toggledItemSelector, toggledClass, triggerSelector) {
244
+
245
+ // CSSクラスをトグルする対象要素を特定する。(与えられたセレクタで複数要素が該当しても先頭要素のみを対象にする。これは既存を踏襲)
246
+
247
+ const toggledItem = document.querySelector(toggledItemSelector);
248
+
249
+ if (!toggledItem) return;
250
+
251
+
252
+
253
+ // CSSクラスをトグルする関数
254
+
255
+ const handleToggle = () => {
256
+
257
+ toggledItem.classList.toggle(toggledClass);
258
+
259
+ }
260
+
261
+
262
+
263
+ // トグル実行のトリガーとなる要素に、トグルを実行する関数をaddEventListenerする。
264
+
265
+ document.querySelectorAll(triggerSelector).forEach(triggerElm => {
266
+
267
+ triggerElm.addEventListener('click', handleToggle);
268
+
269
+ });
270
+
271
+ }
272
+
273
+ ```
274
+
275
+ 質問にある例に適用するには
276
+
277
+ ```javascript
278
+
279
+ addClassToggleOnTriggers('.js-open', 'is-opened', '.header__nav-btn, .cover-darken');
280
+
281
+ ```
282
+
283
+
284
+
285
+ とします。➡ [サンプル3](https://codepen.io/i8086x/pen/powBqoe?editors=1010)

3

テキスト追加

2021/10/02 16:10

投稿

退会済みユーザー
test CHANGED
@@ -198,4 +198,4 @@
198
198
 
199
199
 
200
200
 
201
- ですが、こんな怪しげなまがいものを自作して、プチ[John Resig](https://ja.wikipedia.org/wiki/%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%BB%E3%83%AC%E3%82%B7%E3%82%B0)になった自己満足に浸るヒマがあったら、[本物](https://jquery.com/)を使えるようになったほうが然いいです。ということで、別案を提示するつもりが、質問の本題からはそれて、「jQueryを覚えてみては?」というのが結論になってしまいました。申し訳ありません。
201
+ ですが、こんな怪しげなまがいものを自作して、プチ[John Resig](https://ja.wikipedia.org/wiki/%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%BB%E3%83%AC%E3%82%B7%E3%82%B0)になった自己満足に浸るヒマがあったら、[本物](https://jquery.com/)を使えるようになったほうが然いいです。ということで、別案を提示するつもりが、質問の本題からはそれて、「jQueryを覚えてみては?」というのが結論になってしまいました。申し訳ありません。

2

コード追加

2021/10/02 00:56

投稿

退会済みユーザー
test CHANGED
@@ -99,3 +99,103 @@
99
99
 
100
100
 
101
101
  のような名前になるかと思います。
102
+
103
+
104
+
105
+
106
+
107
+ ### 別案
108
+
109
+
110
+
111
+ jQueryもどきを自作してみる、っていうのはどうでしょう?
112
+
113
+
114
+
115
+ ```javascript
116
+
117
+ (function () {
118
+
119
+
120
+
121
+ function myQuery(selector) {
122
+
123
+ const nodeList = document.querySelectorAll(selector);
124
+
125
+ return new myQuery.__clazz__(nodeList);
126
+
127
+ }
128
+
129
+
130
+
131
+ myQuery.__clazz__ = class {
132
+
133
+
134
+
135
+ constructor(nodeList) {
136
+
137
+ this.nodeList = nodeList;
138
+
139
+ }
140
+
141
+
142
+
143
+ on(eventType, handler) {
144
+
145
+ this.nodeList.forEach(node => {
146
+
147
+ node.addEventListener(eventType, handler);
148
+
149
+ });
150
+
151
+ }
152
+
153
+
154
+
155
+ toggleClass(cssClass) {
156
+
157
+ this.nodeList.forEach(node => {
158
+
159
+ if (node.classList) {
160
+
161
+ node.classList.toggle(cssClass);
162
+
163
+ }
164
+
165
+ });
166
+
167
+ }
168
+
169
+ }
170
+
171
+
172
+
173
+ window.$ = myQuery;
174
+
175
+ })();
176
+
177
+
178
+
179
+ ```
180
+
181
+ ってなものを作っておいて、質問にある例に適用するならば、
182
+
183
+ ```javascript
184
+
185
+ $('.header__nav-btn, .cover-darken').on('click', function() {
186
+
187
+ $('.js-open').toggleClass('is-opened');
188
+
189
+ });
190
+
191
+ ```
192
+
193
+ とすると。
194
+
195
+
196
+
197
+ 先のサンプルに対して、このjQueryもどきを使うと、こんなん。➡ [サンプル2](https://codepen.io/i8086x/pen/ZEyZXjE?editors=1010)
198
+
199
+
200
+
201
+ ですが、こんな怪しげなまがいものを自作して、プチ[John Resig](https://ja.wikipedia.org/wiki/%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%BB%E3%83%AC%E3%82%B7%E3%82%B0)になった自己満足に浸るヒマがあったら、[本物](https://jquery.com/)を使えるようになったほうが当然いいです。ということで、別案を提示するつもりが、質問の本題からはそれて、「jQueryを覚えてみては?」というのが結論になってしまいました。申し訳ありません。

1

テキスト追加

2021/10/02 00:30

投稿

退会済みユーザー
test CHANGED
@@ -85,3 +85,17 @@
85
85
  また、
86
86
 
87
87
  - トリガー指定のあるセレクタに、複数の要素が該当した場合(たとえば上記のサンプルでは、`.B` ボタン は二つあります。)、そのすべてに、クリックハンドラとして、トグル実行の関数をクリックリスナーとして設定します。
88
+
89
+
90
+
91
+ **補足:**
92
+
93
+
94
+
95
+ 上記の修正だと、トグルするCSSクラス名(`"is-open"`)も引数で与えるため、関数名の`menu_open()`も変える必要がありそうです。たとえば
96
+
97
+  - `addClassToggleOnTriggers()`
98
+
99
+
100
+
101
+ のような名前になるかと思います。