如何解决如何测试上下文菜单是否打开或显示?
我有一个页面,其中包含几个 pre
标记,其中包含计算机代码。我有一个 mouseover
事件侦听器,它突出显示了 pre
标记中的所有代码。我还让它删除了 mouseout
事件上的突出显示。如果您使用键盘进行复制 (ctrl-C),效果会非常好。
但是如果你想右键单击并从上下文菜单中复制,就有问题了。鼠标进入上下文菜单的那一刻,它会触发 mouseout
标记的 pre
事件。
我需要一种方法来测试上下文菜单当前是否打开或显示。然后我可以取消删除突出显示。有没有办法测试上下文菜单是打开还是显示?
我不想要任何 jquery,拜托了。
我对这个问题的最后一个选择可能是 oncontextmenu
,但我不知道如何知道它是否关闭。除非我为上下文菜单的 mouseout
事件尝试事件侦听器(如果可能)。
这是我目前的代码:
window.onload = function(){
function selectText(element) {
var range,selection;
if(window.getSelection) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
}
function unSelectText() {
window.getSelection().removeAllRanges();
}
preTags = document.getElementsByTagName('PRE');
for(var i = 0; i < preTags.length; i++) {
preTags[i].onmouseover = function() {selectText(this)};
preTags[i].onmouseout = function() {unSelectText(this)};
}
codeTags = document.getElementsByTagName('CODE');
for(var i = 0; i < codeTags.length; i++) {
codeTags[i].onmouseover = function() {selectText(this)};
codeTags[i].onmouseout = function() {unSelectText(this)};
}
};
解决方法
我知道这并不是您特定问题的真正答案,但从用户体验的角度来看,在悬停时选择文本可能会令人困惑。用户可能认为这只是一种悬停效果。最好在文本旁边有一个“复制”按钮或单击要复制的文本。这带来了额外的好处,即用户只需点击一次。
document.getElementById("code").addEventListener("click",function () {
navigator.clipboard.writeText(this.innerText);
});
#code {
cursor: pointer;
display: inline-block;
padding: 15px;
border: solid 1px black;
}
<pre id="code">
print("hello,world")
print("hello,world")
</pre>
<p>Click to copy.</p>
由于 JS 中没有事件来触发上下文菜单框的关闭操作,也没有可靠的解决方法:据我从不同的研究中了解到,您的问题的答案是否定的。
但是,如果您考虑类似的方法,可以使用自定义上下文菜单来解决您的问题。
简短说明
- 向您的
code
和pre
元素添加自定义上下文菜单。在您的情况下,只需要一个项目copy
(在示例中快速!非常!简化!演示为简单框)。 - 打开菜单时停用右键单击以避免停止选择您的代码(据我了解是您的问题/问题)
- 打开菜单时禁用右键单击其他元素,这样您就不会打开其他上下文菜单
- 当点击菜单项时开始复制动作关闭并重置
- 点击菜单外关闭并重置
简化示例
请注意:该示例是一个快速、肮脏且非常简单的示例,用于演示该技术。我肯定需要适应您的特殊项目。
window.onload = function(){
// general vars
let isOpenContextMenu = false;
const $contextMenu = document.getElementById('contextMenu');
// all code/pre elements in one object to use in one loop
const $codeElements = document.querySelectorAll('pre,code');
let $actualCodeElement = {};
// methods
function selectText(element) {
var range,selection;
if(window.getSelection) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
}
}
function unSelectText() {
window.getSelection().removeAllRanges();
}
// listeners
// block right clicke when context menu on code/pre element is open
function listenerContextMenuBlocked(evt){
evt.preventDefault();
}
// clicks when context menu on code/pre elements is open
function listenerMenuClick(ev){
let $clickedElement = ev.target;
do{
if($clickedElement == $contextMenu){
// clicked on context menu
// --> copy action
let codeToCopy = $actualCodeElement.innerText;
let temporaryInput = document.createElement('input');
temporaryInput.type = 'text';
temporaryInput.value = codeToCopy;
document.body.appendChild(temporaryInput);
temporaryInput.select();
document.execCommand('Copy');
document.body.removeChild(temporaryInput);
// --> close menu and reset
$contextMenu.classList.remove('contextMenu--active');
isOpenContextMenu = false;
window.removeEventListener('contextmenu',listenerContextMenuBlocked);
return;
}
$clickedElement = $clickedElement.parentNode;
} while($clickedElement)
// clicked outside context menu
// --> close and reset
$contextMenu.classList.remove('contextMenu--active');
isOpenContextMenu = false;
window.removeEventListener('contextmenu',listenerContextMenuBlocked);
}
// open custom context menu when right click on code/pre elements
function listenerOpenContextMenuCodeBlock(e) {
e.preventDefault();
// used to copy conten in listenerMenuClick()
$actualCodeElement = e.target;
if(false === isOpenContextMenu){
// open context menu
$contextMenu.style.top = e.clientY + 2 + 'px';
$contextMenu.style.left = e.clientX + + 2 + 'px';
$contextMenu.classList.add('contextMenu--active');
isOpenContextMenu = true;
window.addEventListener('click',listenerMenuClick);
window.addEventListener('contextmenu',listenerContextMenuBlocked);
}
}
for(var i = 0; i < $codeElements.length; i++) {
//$actualElement = $codeElements[i]; //
$codeElements[i].addEventListener('contextmenu',listenerOpenContextMenuCodeBlock);
$codeElements[i].onmouseover = function() {
if(false === isOpenContextMenu){
selectText(this)
}
};
$codeElements[i].onmouseout = function() {
if(false === isOpenContextMenu){
unSelectText(this)
}
};
}
};
/* styles needed for custom context menu */
html {
position: relative;
}
#contextMenu {
display: none;
position: absolute;
font-family: sans-serif;
font-size: 11px;
line-height: 12px;
padding: 2px 5px;
background-color: #eeeeee;
border: 1px solid #a5a5a5;
box-shadow: 2px 3px 1px -1px rgba(0,0.4);;
cursor: context-menu;
z-index: 10;
}
#contextMenu:hover {
background-color: #aad7f3;
}
#contextMenu.contextMenu--active {
display: block;
}
<!-- SIMPLIFIED custom context menu for code/pre elements = hidden on page -->
<nav id="contextMenu" style="top: 50px; left: 30px" >
<div>Copy Codeblock</div>
</nav>
<!-- example elements for code presentation / testing -->
<pre>
Lorem,ipsum dolor sit amet consectetur adipisicing elit.
Provident magni blanditiis,ea necessitatibus esse nihil,quae iste explicabo beatae perspiciatis quibusdam tempora minima,eos molestias illum voluptatum voluptate ipsum perferendis!
</pre>
<code>
Li Europan lingues es membres del sam familie. Lor separat existentie
es un myth. Por scientie,musica,sport etc,litot Europa usa li sam vocabular.
Li lingues differe solmen in li grammatica,li pronunciation e li plu commun vocabules.
Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar
custosi traductores.
</code>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。