contextmenuで独自メニューを表示するような場合、ページ内のテキストがコピーできなくなったという苦情が寄せられることがあります。
Ctrl+Cなどのショートカットを利用すればコピーは可能ですが、利用者にショートカットキーを強要するのも悩ましいので、テキスト選択中にはデフォルトのメニューを表示するように工夫してみます。
テキスト選択中はデフォルトのコンテキストメニューを表示する
テキストが選択中かどうかは、window.getSelection() の戻り値で判別ができます。テキストが選択されるタイミングは keyup/mouseup のいずれかと思われるので、それぞれのイベントで選択中であるかをチェックします。
var hasSelection = false;
$(document).on("keyup mouseup", function(e) {
hasSelection = (window.getSelection().toString() !== "")? true : false;
});
メニュー表示時に、テキスト選択中であれば独自処理に遷移しないようにします。
$(document).on("contextmenu", function(e) {
if (hasSelection) return; // テキスト選択中であれば何もしない
// 以降は独自メニューを表示するなどの処理
...
});
簡単にチェックするだけであれば、これでよいのですがいくつか問題があるので解決します。
改修
選択したテキスト上以外でもデフォルトメニューが開く
最後にkeyup/mouseupしたタイミングでテキスト選択状態を判別しているので、右クリックした位置が全く違う場所でもデフォルトメニューが開きます。右クリックした瞬間に選択状態を判定すればよさそうですが、Google Chromeなどではテキスト上で右クリックすると自動選択されてしまうため、正しく判別できません。
そのため、多少強引ですがRangeオブジェクトの各プロパティを比較して、前回keyup/mousedown時と右クリック時で選択範囲が同じ場合のみデフォルトとなるよう変更します。
// keyup, mouseupのタイミングで文字列選択があるか判別
var lastSelection = null;
$(document).on("keyup mouseup", function(e) {
lastSelection = (window.getSelection().toString() !== "")? window.getSelection().getRangeAt(0) : null;
});
// カスタム右クリックメニュー
$(document).on("contextmenu", function(e) {
// テキスト選択中であれば何もしない
if (lastSelection !== null) {
var isSame = true;
var currentSelection = window.getSelection().getRangeAt(0);
for (var key in currentSelection) {
if (currentSelection[key] != lastSelection[key]) {
isSame = false;
break;
}
}
if (isSame) return;
}
// 以降は独自メニューを表示するなどの処理
...
});
IE8以下への対応
window.getSelection()はIE9以降でのみ対応されているため、それっぽく動作するように代わりの処理を実装します。
// IE8以下対応
if (!window.getSelection) {
window.getSelection = function() {
return {
toString: function() {
return document.selection.createRange().text;
},
getRangeAt: function() {
return {};
}
}
}
}
デモ
完成した動作はこんな感じです。
Chrome 36/Firefox 30/IE 8-11で動作確認しました。