问题 如何处理源自鼠标点击而不是键盘操作的光标活动?


当用户通过鼠标单击将光标移动到另一个位置时,我想做某事,但是当通过键盘操作(箭头,pageup / pagedown,home / end)完成时,我不想这样做。

  • 我不能只听 cursorActivity 因为它会触发键盘和鼠标操作。
  • 我不确定我能不能听 mousedown,因为它可能是一个不是光标位置变化的东西的开始(例如选择,拖动)。

捕获那些鼠标起源的光标移动的最佳方法是什么?


13087
2017-10-27 11:03


起源

a user moves the cursor to another location via a mouse click 你能解释一下吗? - Ivanka Todorova
@IvankaTodorova如果从标签上看不清楚:这是一个询问问题的问题 CodeMirror编辑器。 “另一个位置”是指用户通过鼠标单击编辑器中的某些文本来更改当前光标位置。 - Oak
您可以发布代码镜像编辑器的当前设置吗? - Ivanka Todorova
@IvankaTodorova我不确定你的意思是“设置”,无论如何 - 这有关系吗?我没有看到任何配置选项来控制这样的事情。 - Oak


答案:


你可以听这些事件:

  • 鼠标按下
  • cursorActivity
  • KEYDOWN
  • beforeChange

如果满足以下条件,则通过鼠标单击移动光标:

  • 一个 cursorActivity 事件在a之后触发 mousedown 事件
  • 两个事件之间没有按下任何移动键
  • 两个事件之间的内容没有变化
  • 没有选择文字

var movedByMouse = false;

var editor = CodeMirror(document.body);

editor.on("mousedown", function () {
    movedByMouse = true;
});

editor.on("cursorActivity", function () {
    if (movedByMouse) {
        movedByMouse = false;
        if (!editor.getSelection()) {
            console.log("Moved by mouse");
        }
    }
});

editor.on("keydown", function () {
    if (isMovementKey(event.which)) {
        movedByMouse = false;
    }
});

editor.on("beforeChange", function () {
    movedByMouse = false;
});

function isMovementKey(keyCode) {
    return 33 <= keyCode && keyCode <= 40;
};
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/codemirror.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/codemirror.min.js"></script>


6
2017-10-31 12:31



cursorActivity 按下键盘键时触发。你的 keyUpHandler 套 movedByMouse 当一个人在编辑器中输入任何内容时为true。 - Ivanka Todorova
@IvankaTodorova - 非常好点。 - ConnorsFan
我更新了代码片段,以解释由对内容所做的更改触发的光标移动(例如,在键入文本时)。 - ConnorsFan
你能听吗? 所有 cursorActivity事件,获取当前光标位置并仅过滤源自之后的那些事件 onmouseup光标没有改变位置的地方? - tmslnz
谢谢您的回答!它运行良好,除了它通过拖动选择文本时触发每个光标更改。 - Oak


所有鼠标事件(我知道):

  • 的onclick
  • oncontextmenu
  • ondbclick
  • onmousedown事件
  • OnMouseEnter在
  • OnMouseLeave在
  • 的OnMouseMove
  • 的onmouseover
  • 的onmouseout
  • onmouseup

所以,基本上,没有解决你的问题,在javascript AFAIK。但是,您可以使用第二个建议并跟踪事件,并根据它们选择是否执行您的操作。

var editorElement = document.body;
var codeMirror = CodeMirror(editorElement);
var mouseEvents = [
  'click',
  'contextmenu',
  'dbclick',
  'mousedown',
  'mouseenter',
  'mouseleave',
  'mousemove',
  'mouseover',
  'mouseout',
  'mouseup'
];
var mouseEventsLog = [];
var lastCursorPosition = null;
var doAction = function() {
  var previousEvent = mouseEventsLog[mouseEventsLog.length - 2];
  var lastEvent = mouseEventsLog[mouseEventsLog.length - 1];

  if (((previousEvent === 'mousedown' && lastEvent === 'mouseup') || lastEvent === 'click') &&codeMirror.getSelection().length <= 0) {
    if (codeMirror.getCursor() !== lastCursorPosition) {
      console.log('Cursor changed by clicking.');
    }
  }

  lastCursorPosition = codeMirror.getCursor();
};
for (var i = 0; i < mouseEvents.length; i++) {
  editorElement.addEventListener(mouseEvents[i], function(event) {
    mouseEventsLog.push(event.type);
    if (event.type == 'mouseup') {
      doAction();
    }
  }, false);
}
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="http://codemirror.net/mode/javascript/javascript.js"></script>
<link href="https://codemirror.net/lib/codemirror.css" rel="stylesheet"/>

注意:我尝试过组合 cursorActivity 和鼠标事件,但 cursorActivity 在他们面前执行?我其实不确定那里发生了什么= D.


4
2017-10-31 13:22



谢谢你的解决方案!表面上它运作良好,虽然doAction()中的条件看起来不是很强大。例如,如果用户在mousedown和mouseup事件之间稍微移动鼠标 - 但不足以启动选择 - 它将无法触发。而且, codeMirror.getSelection().length <= 0 bit表示如果存在选定区域,然后用户在该区域外单击,则不会计算该单击。 - Oak


您可以使用钩子挂钩到CodeMirror,并定义您自己的事件,当鼠标单击编辑器时光标发生变化时触发该事件

CodeMirror.defineInitHook(function (editor) {
    editor.cursorDidChange = false;
    $(editor.getWrapperElement()).on({
        mousedown : function() {
            if (editor.cursorDidChange) CodeMirror.signal(editor, 'cursorClick');
        },
        mouseup : function() {
            editor.cursorDidChange = false;
        }
    });
    editor.on('cursorActivity', function(e) {
        if (e.isSelection) editor.cursorDidChange = true;
    });
    editor.on('beforeSelectionChange', function(e, range) {
        var start = range.ranges[0].anchor, end = range.ranges[0].head;
        e.isSelection = range.origin == '*mouse' && start.line == end.line && start.ch == end.ch;
    })
});

这使用一个标志和一个计时器来捕获两个事件,如果它们在彼此的短时间内发生,因为点击处理程序在cursorActivity处理程序之后立即触发。

以下是如何将新定义的事件与CodeMirror一起使用的工作示例:

/* Create Hook */
CodeMirror.defineInitHook(function (editor) {
	editor.cursorDidChange = false;
	$(editor.getWrapperElement()).on({
    	mousedown : function() {
            if (editor.cursorDidChange) CodeMirror.signal(editor, 'cursorClick');
        },
        mouseup : function() {
            editor.cursorDidChange = false;
        }
    });
    editor.on('cursorActivity', function(e) {
    	if (e.isSelection) editor.cursorDidChange = true;
    });
    editor.on('beforeSelectionChange', function(e, range) {
        var start = range.ranges[0].anchor, end = range.ranges[0].head;
        e.isSelection = range.origin == '*mouse' && start.line == end.line && start.ch == end.ch;
    })
});
/* -------------- */
/* Create an editor to test it */

var $this = $('.code').eq(0),
    $code = $this.html(),
    $unescaped = $('<div/>').html($code).text();
        
$this.empty();
    
var editor = CodeMirror($this.get(0), {
    value       : $unescaped,
    mode        : 'javascript',
    lineNumbers : true,
    readOnly    : false
});

/* Lets test out the new event */
editor.on('cursorClick', function() {
	$('<div />', {text : 'Cursor moved when clicked !'}).appendTo('#result')
        .show(1).delay(1000).fadeOut(function() {
         	$(this).remove();
        });
});
body {background: #eee;}
.code {margin: 10px 0;}
#result {color: green;}
.CodeMirror {height: auto!important;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://codemirror.net/lib/codemirror.js"></script>
<script src="https://codemirror.net/mode/javascript/javascript.js"></script>
<link href="https://codemirror.net/lib/codemirror.css" rel="stylesheet"/>
<!-- EDITOR -->
<div class="code">test.fn = test.prototype = {
    display : function() {}
        console.log("Move cursor by clicking");
        console.log("Move cursor with keys");
    },
	pushStack: function( elems ) {
		// This is just pseudocode to have something to test
	}
    return false;
}</div>
<!-- EDITOR END -->
<div id="result"></div>


3
2017-11-06 13:14



谢谢你的回答!通过定义新事件看​​起来很棒且非常简洁。但是,当mousedown和mouseup之间的时间超过300ms时,它似乎无法触发,因为mousedown是导致光标更改的那个,而mouseup正在触发onclick。此外,您的解决方案将考虑双击或三击以选择2或3个单独的cursorClick事件,即使所有单击都在跨度结束时完成,因此光标在技术上不会更改位置。 - Oak
@Oak - 然后我想我必须稍微改变方法,丢弃计时器,而是检查选择等。看看是否解决了任何问题。 - adeneo
更好!不幸的是,它现在也在启动选择时触发,但说实话,从技术上讲,它是光标位置的变化。 - Oak
是的,如果你点击其他地方开始选择,它会在光标移动时触发。如果从光标当前所在的位置开始选择,则不应触发。我认为没有办法区分只是移动光标和移动光标以开始选择,除了检查是否在事后做出选择,这将导致逻辑​​上的显着延迟。 - adeneo
遗憾的是,但是我看不出有什么方法可以做出这样的区分:-(我喜欢这个解决方案,虽然我最终用一个更简单的解决方案来接受答案。非常感谢!顺便说一下,你的解决方案没有捕获你选择一些文本然后在选择内部点击的情况 - 我不知道为什么。 - Oak