回答編集履歴

2

コメント (仕様変更) を受けての追記

2020/10/13 02:40

投稿

thyda.eiqau
thyda.eiqau

スコア2982

test CHANGED
@@ -205,3 +205,299 @@
205
205
  });
206
206
 
207
207
  ```
208
+
209
+
210
+
211
+ ## Oct 13, 2020 11:40AM - コメント (仕様変更) を受けての追記
212
+
213
+ ```html
214
+
215
+ <div class="card">
216
+
217
+ <a href="#foo" class="stretched-link">
218
+
219
+ <img src="https://placehold.jp/250x200.png">
220
+
221
+ <div class="card-body">
222
+
223
+ <span role="heading" class="card-title">画像タイトル</span>
224
+
225
+ <span class="card-author" data-href="#author">著者名:ふかみ</span>
226
+
227
+ <span class="card-tag" data-href="#tags">タグ :夜景・ビル・綺麗</span>
228
+
229
+ </div>
230
+
231
+ </a>
232
+
233
+ </div>
234
+
235
+ ```
236
+
237
+
238
+
239
+ ```css
240
+
241
+ .card {
242
+
243
+ position: relative;
244
+
245
+ width: 250px;
246
+
247
+ border: 1px solid gray;
248
+
249
+ padding: 5px;
250
+
251
+ margin: 0 0 100px 0;
252
+
253
+ }
254
+
255
+ .card a {
256
+
257
+ text-decoration: none;
258
+
259
+ }
260
+
261
+ .card a:active {
262
+
263
+ color: rgb(238, 0, 0);
264
+
265
+ color: -webkit-link;
266
+
267
+ }
268
+
269
+ .card-body {
270
+
271
+ display: flex;
272
+
273
+ flex-direction: column;
274
+
275
+ justify-content: flex-start;
276
+
277
+ align-items: flex-start;
278
+
279
+ }
280
+
281
+ .card-body > span {
282
+
283
+ line-height: 2;
284
+
285
+ }
286
+
287
+ .card-body > span[role="heading"] {
288
+
289
+ font-size: 1.17em;
290
+
291
+ font-weight: bold;
292
+
293
+ }
294
+
295
+ .card-body > span:hover {
296
+
297
+ text-decoration: underline;
298
+
299
+ }
300
+
301
+ .card-body > span.clicking {
302
+
303
+ color: rgb(238, 0, 0);
304
+
305
+ color: -webkit-activelink;
306
+
307
+ }
308
+
309
+ ```
310
+
311
+
312
+
313
+ ```js
314
+
315
+ const threshold = 150; // ms
316
+
317
+
318
+
319
+ /**
320
+
321
+ * .card-body > [data-href] の要素にマウスが乗ったときに、ベースとなるaタグのhref属性値を書き換える
322
+
323
+ * @param Element baseAnchor ベースとなるaタグ
324
+
325
+ * @param String customHref リンク先
326
+
327
+ */
328
+
329
+ const onMouseOverCustomLinkElement = (baseAnchor, customHref) => {
330
+
331
+ baseAnchor.dataset.originalHref = baseAnchor.href;
332
+
333
+ baseAnchor.href = customHref;
334
+
335
+ };
336
+
337
+
338
+
339
+ /**
340
+
341
+ * .card-body > [data-href] の要素からマウスが外れたときに、ベースとなるaタグのhref属性値を元に戻す
342
+
343
+ * @param Element baseAnchor ベースとなるaタグ
344
+
345
+ */
346
+
347
+ const onMouseOutCustomLinkElement = baseAnchor => {
348
+
349
+ baseAnchor.href = baseAnchor.dataset.originalHref;
350
+
351
+ delete baseAnchor.dataset.originalHref;
352
+
353
+ };
354
+
355
+
356
+
357
+ /**
358
+
359
+ * .card-body > span の要素がクリックされたときに、aタグがクリックされたときと同様の色になるようにする
360
+
361
+ * @param Element customLinkSpan .card-body > span の要素
362
+
363
+ */
364
+
365
+ const onMouseDownCustomLinkElement = customLinkSpan => {
366
+
367
+ customLinkSpan.classList.add('clicking');
368
+
369
+ };
370
+
371
+
372
+
373
+ /**
374
+
375
+ * なんらかの場所でクリック解除されたときに、.card-body > span の要素が、
376
+
377
+ * aタグがクリック解除されたときと同様の色になるようにする
378
+
379
+ */
380
+
381
+ const onMouseUpDocumentBody = () => {
382
+
383
+ [...document.querySelectorAll('.card-body > span')].forEach(span => span.classList.remove('clicking'));
384
+
385
+ };
386
+
387
+
388
+
389
+ /**
390
+
391
+ * card の中でクリックされたとき、通常のクリックかそうでないか
392
+
393
+ * (ロングタップやドラッグ) を判定するために、クリック開始時刻を保持する
394
+
395
+ * @param Element card
396
+
397
+ */
398
+
399
+ const onMouseDownCard = card => {
400
+
401
+ card.dataset.clickBegan = (new Date()).getTime();
402
+
403
+ };
404
+
405
+
406
+
407
+ /**
408
+
409
+ * card の中でクリック解除されたとき、通常のクリックでないならリンク先への遷移を止める
410
+
411
+ * @param Element card
412
+
413
+ */
414
+
415
+ const onClickCard = (card, evt) => {
416
+
417
+ // mouseup した時間が mousedown の thresholdミリ秒以内なら、クリックとみなす
418
+
419
+ const clickedMilliseconds = (new Date()).getTime() - card.dataset.clickBegan;
420
+
421
+ const isClick = clickedMilliseconds < threshold;
422
+
423
+ if(!isClick) {
424
+
425
+ evt.preventDefault();
426
+
427
+ console.log('prevented');
428
+
429
+ }
430
+
431
+ };
432
+
433
+
434
+
435
+ const assignEvent = card => {
436
+
437
+ // eventの順番は mousedown > mouseup > click
438
+
439
+
440
+
441
+ // リンク先を変化させるイベントを登録
442
+
443
+ [...card.querySelectorAll('.card-body > [data-href]')].forEach(span => {
444
+
445
+ const a = card.querySelector(':scope > a');
446
+
447
+ span.addEventListener('mouseover', () => {
448
+
449
+ onMouseOverCustomLinkElement(a, span.dataset.href);
450
+
451
+ });
452
+
453
+ span.addEventListener('mouseout', () => {
454
+
455
+ onMouseOutCustomLinkElement(a);
456
+
457
+ });
458
+
459
+ });
460
+
461
+
462
+
463
+ // .card-body > span にリンクのようなスタイルを適用するイベントを登録
464
+
465
+ [...card.querySelectorAll('.card-body > *')].forEach(span => {
466
+
467
+ span.addEventListener('mousedown', () => {
468
+
469
+ onMouseDownCustomLinkElement(span);
470
+
471
+ });
472
+
473
+ });
474
+
475
+
476
+
477
+ // カード全体のクリック関連イベントを登録
478
+
479
+ card.addEventListener('mousedown', () => {
480
+
481
+ onMouseDownCard(card);
482
+
483
+ });
484
+
485
+ card.addEventListener('click', evt => {
486
+
487
+ onClickCard(card, evt);
488
+
489
+ });
490
+
491
+ }
492
+
493
+
494
+
495
+ [...document.querySelectorAll('.card')].forEach(card => {
496
+
497
+ assignEvent(card);
498
+
499
+ });
500
+
501
+ document.body.addEventListener('mouseup', onMouseUpDocumentBody);
502
+
503
+ ```

1

コメントを受けてJSを修正

2020/10/13 02:40

投稿

thyda.eiqau
thyda.eiqau

スコア2982

test CHANGED
@@ -120,6 +120,10 @@
120
120
 
121
121
  card.addEventListener('mouseup', evt => {
122
122
 
123
+ const anchorsParentClass = ['card-title', 'card-author', 'card-tag'];
124
+
125
+
126
+
123
127
  // mouseup した時間が mousedown の thresholdミリ秒以内なら、クリックとみなす
124
128
 
125
129
  const clickedMilliseconds = (new Date()).getTime() - card.dataset.clickBegan;
@@ -128,40 +132,42 @@
128
132
 
129
133
 
130
134
 
135
+ // 前回の判定のときに保持させていた、クリックを許すかどうかの値を削除する
136
+
137
+ [...card.querySelectorAll(`.${anchorsParentClass.join(' > a, .')} > a`)].forEach(link => {
138
+
139
+ delete link.dataset.isDisabled;
140
+
141
+ });
142
+
143
+
144
+
131
145
  // cardの中のどの要素が実際クリックされたのかを判定
132
146
 
133
147
  // 動作させたいリンクは .card-title, .card-author, .card-tag のいずれかの子要素
134
148
 
135
- switch(evt.target.parentNode.className) {
149
+ if(anchorsParentClass.indexOf(evt.target.parentNode.className) !== -1) {
136
-
137
- case 'card-title':
150
+
138
-
139
- case 'card-author':
140
-
141
- case 'card-tag':
142
-
143
- // このリンクを動作させるかどうか保持させる
151
+ // このリンクを動作させるかどうか保持させる
144
-
152
+
145
- evt.target.dataset.isDisabled = !isClick;
153
+ evt.target.dataset.isDisabled = !isClick;
146
-
154
+
147
- break;
155
+ return;
148
-
149
-
150
-
151
- default:
152
-
153
- // リンク以外の場所でマウスが開放されて、かつクリックとみなす動作だった場合は
154
-
155
- // .card-title > a を発火させる
156
-
157
- if(isClick) {
158
-
159
- card.querySelector('.card-title > a').click();
160
-
161
- }
162
156
 
163
157
  }
164
158
 
159
+
160
+
161
+ // リンク以外の場所でマウスが開放されて、かつクリックとみなす動作だった場合は
162
+
163
+ // .card-title > a を発火させる
164
+
165
+ if(isClick) {
166
+
167
+ card.querySelector('.card-title > a').click();
168
+
169
+ }
170
+
165
171
  });
166
172
 
167
173