I want to do something when a user moves the cursor to another location via a mouse click, but not do it when it's done via a keyboard action (arrows, pageup/pagedown, home/end).
cursorActivity
since it triggers on both keyboard and mouse actions.mousedown
, because it might be the start of something which is not a cursor location change (e.g. selecting, dragging).What's the best way to catch those mouse-originated cursor movements?
You can hook into CodeMirror by using a hook, and define your own event that fires when the cursor changes from a mouse click on the editor
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;
})
});
This uses a flag and a timer to catch both events if they happen within a short time of each other, as the click handler fires right after the cursorActivity handler.
Here's a working example of how to use the newly defined event with 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>
You could listen to these events:
The cursor was moved by a mouse click if these conditions are met:
cursorActivity
event is triggered after a mousedown
eventvar 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>
All Mouse Events (that I know of):
So, basically, there's no solution for your problem that's in javascript AFAIK. However, you can go with your second suggestion and track events and depending on them to choose whether to execute your action or not.
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"/>
Note: I tried combining cursorActivity
and mouse events, but cursorActivity
is executed before them? I'm actually not sure what happens there =D
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With