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

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

新規登録して質問してみよう
ただいま回答率
85.35%
JavaScript

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

jQuery

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

CSS

CSSはXMLやHTMLで表現した色・レイアウト・フォントなどの要素を指示する仕様の1つです。

Q&A

解決済

1回答

1844閲覧

<a>タグを入れ子のように挙動させながら、そのテキストを選択できるようにしたい

kurazushi

総合スコア43

JavaScript

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

jQuery

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

CSS

CSSはXMLやHTMLで表現した色・レイアウト・フォントなどの要素を指示する仕様の1つです。

0グッド

0クリップ

投稿2020/10/12 13:10

編集2020/10/12 18:54

###実現したいこと
下図のように<a>タグを入れ子にせずに、入れ子にしたかのような機能にしたいです。
しかし、難しい条件がございます。

イメージ説明

###条件
条件として次の機能1から4を求めておりまして、各<a>タグは通常の<a>タグと同じ挙動での実現を求めています。(テキストを選択できないなどの挙動の違いがないようにしたいです。)

機能1. カード全体のクリックで「画像タイトル」のリンクへ飛ぶこと
機能2.「著者名」「タグ」のクリックでそれぞれのリンクへ飛ぶこと
機能3. aタグの右クリックメニュー(新しいタブで開く等)を出せること
機能4. テキストをカーソルで選択できること(コピペできないと使いにくいため)

###実装した機能

機能123をCSSで実装しました。
「画像タイトル」のリンク範囲をCSSの:after.card全体に伸ばしたわけです。
(これは昨日の質問でご回答いただいた方法です。)

しかしこれによって機能4ができなくなりました。
テキスト選択しようとすると、:afterになっているためにドラッグが発生してしまいます。

そこで下記のJavaScriptを書きました。
テキスト選択の際はis_drag = trueとなり、先のCSSの:afterを解除したという感じになっています。

###発生している問題
これでいけるとかと思いましたが、テキスト選択のタイミングが次のように遅れます。

読み込み終了→テキスト選択→できない→もう一度テキスト選択→できる

このように、一度目の選択ができないのです。

###ソースコード
次のソースコードを実行できるjsfiddle がありますので、よろしければお試しください。

html

1<div class="card is_arrow_link"> 2 <img src="https://placehold.jp/250x200.png"> 3 <div class="card-body"> 4 <h3 class="card-title"> 5 <a href="example.com" class="stretched-link">画像タイトル</a> 6 </h3> 7 <p class="card-author"> 8 <a href="#author">著者名:ふかみ</a> 9 </p> 10 <p class="card-tag"> 11 <a href="#tags">タグ :夜景・ビル・綺麗</a> 12 </p> 13 </div> 14</div>

css

1.card { 2 position: relative; 3 width: 250px; 4 border: 1px solid gray; 5 padding: 5px; 6 margin: 0 0 100px 0; 7} 8 9.card.is_arrow_link .stretched-link::after { 10 position: absolute; 11 top: 0; 12 right: 0; 13 bottom: 0; 14 left: 0; 15 z-index: 1; 16 content: ""; 17 background-color: transparent; 18} 19 20.card-author > a, .card-tag > a { 21 position: relative; 22 z-index: 2; 23} 24 25a { 26 line-height: 2; 27 display: inline-block; 28 text-decoration: none; 29} 30 31a:hover { 32 text-decoration: underline; 33} 34 35h3,p{ 36 margin: 0; 37}

javascript

1// これがtrueのときCSSの:afterを解除して、テキスト選択できるようにしました 2var is_drag = false; 3 4// mousedown 5$(document).on('mousedown','.card', function(){ 6 console.log('mousedown'); 7 console.log('mousedown is_drag =' ,is_drag); 8 $(this).addClass('is_arrow_link'); 9 is_drag = true; 10}); 11 12// mouseup 13$(document).on('mouseup','.card', function(){ 14 console.log('mouseup'); 15 is_drag = false; 16 //$(this).addClass('is_arrow_link'); 17}); 18 19// mousemove 20$(document).on('mousemove', function() { 21 console.log('mousemove'); 22 // drag 23 if (is_drag === true) { 24 console.log('drag'); 25 $('.card').removeClass('is_arrow_link'); 26 } 27});

.card.append()されるため、$(document).on();の方法で書いています。
###試したこと
素人試行錯誤で恐縮ですが、上記JavaScriptのような「ドラッグの検出」をやめて、「長押しの検出」というアプローチを試してみました。

「長押しの検出」はライブラリ「longpress 」を用いて以下前半部分にコピペしています。

しかしこちらも「ドラッグの検出」と同様のタイミングのズレが生じてしまいました。

JavaScript

1/*------------------------------------------------- 2 3 以下は ライブラリ longpress です 4 5--------------------------------------------------*/ 6 7/** 8 * Longpress is a jQuery plugin that makes it easy to support long press 9 * events on mobile devices and desktop borwsers. 10 * 11 * @name longpress 12 * @version 0.1.2 13 * @requires jQuery v1.2.3+ 14 * @author Vaidik Kapoor 15 * @license MIT License - http://www.opensource.org/licenses/mit-license.php 16 * 17 * For usage and examples, check out the README at: 18 * http://github.com/vaidik/jquery-longpress/ 19 * 20 * Copyright (c) 2008-2013, Vaidik Kapoor (kapoor [*dot*] vaidik -[at]- gmail [*dot*] com) 21 */ 22 23(function($) { 24 $.fn.longpress = function(longCallback, shortCallback, duration) { 25 if (typeof duration === "undefined") { 26 duration = 500; 27 } 28 29 return this.each(function() { 30 var $this = $(this); 31 32 // to keep track of how long something was pressed 33 var mouse_down_time; 34 var timeout; 35 36 // mousedown or touchstart callback 37 function mousedown_callback(e) { 38 mouse_down_time = new Date().getTime(); 39 var context = $(this); 40 41 // set a timeout to call the longpress callback when time elapses 42 timeout = setTimeout(function() { 43 if (typeof longCallback === "function") { 44 longCallback.call(context, e); 45 } else { 46 $.error('Callback required for long press. You provided: ' + typeof longCallback); 47 } 48 }, duration); 49 } 50 51 // mouseup or touchend callback 52 function mouseup_callback(e) { 53 var press_time = new Date().getTime() - mouse_down_time; 54 if (press_time < duration) { 55 // cancel the timeout 56 clearTimeout(timeout); 57 58 // call the shortCallback if provided 59 if (typeof shortCallback === "function") { 60 shortCallback.call($(this), e); 61 } else if (typeof shortCallback === "undefined") { 62 ; 63 } else { 64 $.error('Optional callback for short press should be a function.'); 65 } 66 } 67 } 68 69 // cancel long press event if the finger or mouse was moved 70 function move_callback(e) { 71 clearTimeout(timeout); 72 } 73 74 // Browser Support 75 $this.on('mousedown', mousedown_callback); 76 $this.on('mouseup', mouseup_callback); 77 $this.on('mousemove', move_callback); 78 79 // Mobile Support 80 $this.on('touchstart', mousedown_callback); 81 $this.on('touchend', mouseup_callback); 82 $this.on('touchmove', move_callback); 83 }); 84 }; 85}(jQuery)); 86 87 88/*------------------------------------------------- 89 90 以下が自作コードです 91 92--------------------------------------------------*/ 93 94$('.card').longpress(function(e) { 95 // 長押しの場合 96 console.log('longpress'); 97 $(this).removeClass('is_arrow_link'); 98}, 99function(){ 100 // 長押し期間以内に離した場合(普通のクリック) 101 console.log('click'); 102 $(this).addClass('is_arrow_link'); 103}, 100 ); // 長押し期間 104

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hatena19

2020/10/12 13:40

> 各<a>タグは通常の<a>タグと同じ挙動での実現を求めています。 通常の<a>タグ上にマウスカーソルを置いた状態ではテキスト選択はできません。 リンク範囲外からマウスドラッグすれば選択はできますが。 > 尚、aタグの中でaタグを利用する方法というブログ記事に次の3つの方法があります。 > 昨日の質問でご回答いただいた方法 上記のどれも同じ挙動だと思いますが。 (Chromeでの動作確認)
kurazushi

2020/10/12 13:57 編集

>リンク範囲外からマウスドラッグすれば選択はできますが。 すみませんリンク範囲外からでしたね。ご指摘ありがとうございます。まぁスマホも射程ということでご容赦ください。 >上記どれも同じ挙動だと思いますが。 失礼いたしました。削除しておきます。
hatena19

2020/10/12 13:58

なるほど、<a>タグの通常の挙動以外のことを実現したいということですね。 難しそうですね。
kurazushi

2020/10/12 14:18

<a>タグ1つなら「テキスト選択できるのが通常の挙動」ですが、今回のように入れ子のような形ですと「テキスト選択できないのが通常の挙動」となるようで… 入れ子のような形であっても選択できた方が便利と思うのですが、厄介な仕様ですよね。 あれから丸一日かけてもできず難航しています。
guest

回答1

0

ベストアンサー

どーでしょうかね……
もしかしたら実現したい内容の意図を私が理解できていないかもしれませんが……

html

1<div class="card"> 2 <img src="https://placehold.jp/250x200.png"> 3 <div class="card-body"> 4 <h3 class="card-title"> 5 <a href="#foo" class="stretched-link">画像タイトル</a> 6 </h3> 7 <p class="card-author"> 8 <a href="#author">著者名:ふかみ</a> 9 </p> 10 <p class="card-tag"> 11 <a href="#tags">タグ :夜景・ビル・綺麗</a> 12 </p> 13 </div> 14</div>

css

1.card { 2 position: relative; 3 width: 250px; 4 border: 1px solid gray; 5 padding: 5px; 6 margin: 0 0 100px 0; 7} 8 9.card-author > a, .card-tag > a { 10 position: relative; 11 z-index: 2; 12} 13 14a { 15 line-height: 2; 16 display: inline-block; 17 text-decoration: none; 18} 19 20a:hover { 21 text-decoration: underline; 22} 23 24h3,p{ 25 margin: 0; 26}

js

1const threshold = 150; // ms 2 3const assignEvent = card => { 4 // eventの順番は mousedown > mouseup > click 5 6 card.addEventListener('mousedown', evt => { 7 // mousedown の時間を保持 8 card.dataset.clickBegan = (new Date()).getTime(); 9 }); 10 11 card.addEventListener('mouseup', evt => { 12 const anchorsParentClass = ['card-title', 'card-author', 'card-tag']; 13 14 // mouseup した時間が mousedown の thresholdミリ秒以内なら、クリックとみなす 15 const clickedMilliseconds = (new Date()).getTime() - card.dataset.clickBegan; 16 const isClick = clickedMilliseconds < threshold; 17 18 // 前回の判定のときに保持させていた、クリックを許すかどうかの値を削除する 19 [...card.querySelectorAll(`.${anchorsParentClass.join(' > a, .')} > a`)].forEach(link => { 20 delete link.dataset.isDisabled; 21 }); 22 23 // cardの中のどの要素が実際クリックされたのかを判定 24 // 動作させたいリンクは .card-title, .card-author, .card-tag のいずれかの子要素 25 if(anchorsParentClass.indexOf(evt.target.parentNode.className) !== -1) { 26 // このリンクを動作させるかどうか保持させる 27 evt.target.dataset.isDisabled = !isClick; 28 return; 29 } 30 31 // リンク以外の場所でマウスが開放されて、かつクリックとみなす動作だった場合は 32 // .card-title > a を発火させる 33 if(isClick) { 34 card.querySelector('.card-title > a').click(); 35 } 36 }); 37 38 // aタグのクリックイベントをフックする。ここにたどり着いた時点ですでに mouseup の処理は 39 // 終わっているので、 isDisabled に値が入っているはず 40 [...card.getElementsByTagName('a')].forEach(a => { 41 a.addEventListener('click', evt => { 42 console.log('clicked', evt.currentTarget); 43 if(a.dataset.isDisabled === 'true') { 44 evt.preventDefault(); 45 console.log('prevented'); 46 } 47 }); 48 }); 49} 50 51[...document.querySelectorAll('.card')].forEach(card => { 52 assignEvent(card); 53});

Oct 13, 2020 11:40AM - コメント (仕様変更) を受けての追記

html

1<div class="card"> 2 <a href="#foo" class="stretched-link"> 3 <img src="https://placehold.jp/250x200.png"> 4 <div class="card-body"> 5 <span role="heading" class="card-title">画像タイトル</span> 6 <span class="card-author" data-href="#author">著者名:ふかみ</span> 7 <span class="card-tag" data-href="#tags">タグ :夜景・ビル・綺麗</span> 8 </div> 9 </a> 10</div>

css

1.card { 2 position: relative; 3 width: 250px; 4 border: 1px solid gray; 5 padding: 5px; 6 margin: 0 0 100px 0; 7} 8.card a { 9 text-decoration: none; 10} 11.card a:active { 12 color: rgb(238, 0, 0); 13 color: -webkit-link; 14} 15.card-body { 16 display: flex; 17 flex-direction: column; 18 justify-content: flex-start; 19 align-items: flex-start; 20} 21.card-body > span { 22 line-height: 2; 23} 24.card-body > span[role="heading"] { 25 font-size: 1.17em; 26 font-weight: bold; 27} 28.card-body > span:hover { 29 text-decoration: underline; 30} 31.card-body > span.clicking { 32 color: rgb(238, 0, 0); 33 color: -webkit-activelink; 34}

js

1const threshold = 150; // ms 2 3/** 4 * .card-body > [data-href] の要素にマウスが乗ったときに、ベースとなるaタグのhref属性値を書き換える 5 * @param Element baseAnchor ベースとなるaタグ 6 * @param String customHref リンク先 7 */ 8const onMouseOverCustomLinkElement = (baseAnchor, customHref) => { 9 baseAnchor.dataset.originalHref = baseAnchor.href; 10 baseAnchor.href = customHref; 11}; 12 13/** 14 * .card-body > [data-href] の要素からマウスが外れたときに、ベースとなるaタグのhref属性値を元に戻す 15 * @param Element baseAnchor ベースとなるaタグ 16 */ 17const onMouseOutCustomLinkElement = baseAnchor => { 18 baseAnchor.href = baseAnchor.dataset.originalHref; 19 delete baseAnchor.dataset.originalHref; 20}; 21 22/** 23 * .card-body > span の要素がクリックされたときに、aタグがクリックされたときと同様の色になるようにする 24 * @param Element customLinkSpan .card-body > span の要素 25 */ 26const onMouseDownCustomLinkElement = customLinkSpan => { 27 customLinkSpan.classList.add('clicking'); 28}; 29 30/** 31 * なんらかの場所でクリック解除されたときに、.card-body > span の要素が、 32 * aタグがクリック解除されたときと同様の色になるようにする 33 */ 34const onMouseUpDocumentBody = () => { 35 [...document.querySelectorAll('.card-body > span')].forEach(span => span.classList.remove('clicking')); 36}; 37 38/** 39 * card の中でクリックされたとき、通常のクリックかそうでないか 40 * (ロングタップやドラッグ) を判定するために、クリック開始時刻を保持する 41 * @param Element card 42 */ 43const onMouseDownCard = card => { 44 card.dataset.clickBegan = (new Date()).getTime(); 45}; 46 47/** 48 * card の中でクリック解除されたとき、通常のクリックでないならリンク先への遷移を止める 49 * @param Element card 50 */ 51const onClickCard = (card, evt) => { 52 // mouseup した時間が mousedown の thresholdミリ秒以内なら、クリックとみなす 53 const clickedMilliseconds = (new Date()).getTime() - card.dataset.clickBegan; 54 const isClick = clickedMilliseconds < threshold; 55 if(!isClick) { 56 evt.preventDefault(); 57 console.log('prevented'); 58 } 59}; 60 61const assignEvent = card => { 62 // eventの順番は mousedown > mouseup > click 63 64 // リンク先を変化させるイベントを登録 65 [...card.querySelectorAll('.card-body > [data-href]')].forEach(span => { 66 const a = card.querySelector(':scope > a'); 67 span.addEventListener('mouseover', () => { 68 onMouseOverCustomLinkElement(a, span.dataset.href); 69 }); 70 span.addEventListener('mouseout', () => { 71 onMouseOutCustomLinkElement(a); 72 }); 73 }); 74 75 // .card-body > span にリンクのようなスタイルを適用するイベントを登録 76 [...card.querySelectorAll('.card-body > *')].forEach(span => { 77 span.addEventListener('mousedown', () => { 78 onMouseDownCustomLinkElement(span); 79 }); 80 }); 81 82 // カード全体のクリック関連イベントを登録 83 card.addEventListener('mousedown', () => { 84 onMouseDownCard(card); 85 }); 86 card.addEventListener('click', evt => { 87 onClickCard(card, evt); 88 }); 89} 90 91[...document.querySelectorAll('.card')].forEach(card => { 92 assignEvent(card); 93}); 94document.body.addEventListener('mouseup', onMouseUpDocumentBody);

投稿2020/10/12 15:25

編集2020/10/13 02:40
thyda.eiqau

総合スコア2982

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kurazushi

2020/10/12 16:13 編集

どうもありがとうございます。ほぼクリア出来ているように思えます。 テキスト選択はできますし、.cardをクリックしての「画像タイトル」へのリンクも効きました。
kurazushi

2020/10/12 17:22 編集

ただ誠に恐れ入りますが、どうやら「画像タイトル」のテキストを選択した状態につきまして、.cardをクリックしての「画像タイトル」へのリンクが効かなくなってしまいました。この場合は「prevented」がコンソールに出てしまいリンクできないようでした。 そして右クリック時のメニューにつきまして、「新しいタブで開く」でなく、「画像だけを表示」(または「名前を付けてページを保存」)のメニューとなってしまうようでした。
thyda.eiqau

2020/10/12 17:01

> ただ誠に恐れ入りますが、どうやら「画像タイトル」のテキストを選択した状態につきまして、.cardをクリックしての「画像タイトル」へのリンクが効かなくなってしまいました。この場合は「prevented」がコンソールに出てしまいリンクできないようでした。 修正します。 > そして右クリック時のメニューにつきまして、「新しいタブで開く」などのリンクのメニューが表示できないようでした。 ブラウザは何ですか?
kurazushi

2020/10/12 17:20 編集

>修正します。 ご修正どうもありがとうございました!「prevented」の方につきましては無事解消致しました。
kurazushi

2020/10/12 17:22 編集

>ブラウザは何ですか? ブラウザはFirefoxもChromeも同様に「新しいタブで開く」でなく、「画像だけを表示」(または「名前を付けてページを保存」)のメニューでした。 この問題に対しては、右クリックを「oncontextmenu」で判定して、.cardへの右クリックの場合はstretched-linkの右クリックを起動する。という機能でどうかと思ったのですが、左右のクリックを逆に設定しているマウスに対応できないので微妙でしょうか…?
thyda.eiqau

2020/10/12 18:02

画像を右クリックしたときにもリンクを右クリックしたときと同じメニューを出してほしいという意味ですか?それは画像がaタグでくくられていないので無理ですよ。 機能3. aタグの右クリックメニュー(新しいタブで開く等)を出せること は満たしていると思います。画像はaタグの範囲外です。aタグを右クリックしたときには適切なメニューが出てきています。 機能1. カード全体のクリックで「画像タイトル」のリンクへ飛ぶこと は、リンクに飛ぶことが条件なのであって、aタグの機能をすべて満たすこととは記載がありませんので、こちらも満たしていると思うのですが……
kurazushi

2020/10/12 18:55 編集

>機能3. aタグの右クリックメニュー(新しいタブで開く等)を出せること は、質問時のaタグ範囲(after含む)を考えていましたが、そのように記載していませんから伝わるはずもないですね。申し訳ございませんでした。 >画像はaタグの範囲外です。aタグを右クリックしたときには適切なメニューが出てきています。 画像左クリックで「画像タイトル」のリンクにとび、右クリックではそのリンクに飛ぶメニューが出ない。という不整合に納得がいかなかったのですが…、しかしその操作性の不整合は、仰る通り利便性の適切かもしれません。 さてこのたびの質問内容に対して間違いなく正鵠を射るご回答を頂いておりましたため、ベストアンサーとさせて頂きます。右クリックの件、混乱させてしまい申し訳ございませんでした。
kurazushi

2020/10/14 07:38

ありがとうございます。気づくのが遅くなりまして申し訳ございません!ご調整頂いているとは夢にも思いませんでした。 半ば諦めかけていたのですが、おかげ様でやる気を取り戻すことができました。最後まで手厚いご指導心より感謝申し上げます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問