我有一个内容可编辑的div,其跨度如下:
<div contenteditable="true">some <span>spanned</span> text</div>
我想知道是否可以将事件侦听器附加到span
元素本身,该事件侦听器可用于检测插入符号是否在span
元素内移动。
我不是在div
附有监听程序的地方寻找答案,div
每次有活动时都要进行检查,例如此答案的解决方案:
答案 0 :(得分:2)
了解用户输入位置的最简单方法是 document.getSelection()
,它提供了一些节点引用,如 baseNode
和当前 focusNode
以及有用的偏移信息。
然后,您可以在 selectionchange
上使用 document
事件来了解光标何时四处移动,而无需检查鼠标是否正在移动/点击或使用键盘事件。
var out, itm; // saved to spare processor time
document.addEventListener('selectionchange',
function(ev) { // called whenever keycursor moves
// find and save our main HTML objects
if (!out) out = document.getElementById('out');
if (!itm) itm = document.querySelector('span');
// get the user selection informations
let grab = document.getSelection(),
text = grab.baseNode ||0, // <-- clicked here
node = text.parentNode; // <-- container
out.innerHTML = // now print where we're at~
// nowhere near the span, showing a hint
!node?"Well, try moving around the span ..":
// peaking inside the output, wrong way
node == out?"You're grabbing me out ...":
// checked around, let's check for the node
(node == itm?'inside ': // inside span itself
// check if we're selecting the whole node
grab.containsNode(itm)?'found in ':
// check if at least we selected a part of it
grab.containsNode(itm.firstChild)?'partial ':
'other ') + // seemingly somewhere else ...
node.nodeName + // write which node we found
' > ' + // and show a bit of the contents
(text.data || text.innerText).substr(0, 24);
})
body > div {border: 1px solid; font-size: xlarge;}
#out {background: black; color: white; margin: 1em}
div > span {color: red;} * {padding: 1ex 1em}
<div contenteditable>
This is a text before
<span>this span</span>
in middle of the text
</div>
<div id="out">focus NODE > contents ...</div>
现在这可能会因为要检查 100 个节点而变得非常混乱...您不想要绑定/定时侦听器或自定义节点,因此我个人建议使用经典事件侦听器。
您可以在选择开始节点上生成 caret
事件,使用:
document.addEventListener('selectionchange', ev => document.getSelection().baseNode.parentNode.dispatchEvent(new Event('caret')))
然后使用经典的 HTML 侦听器:
<块引用>span.addEventListener('caret', function callback() { /* ... */ });
请注意,您必须检查 neightbor-HTML-children 中的插入符号移动,以了解使用此方法是否超出您的范围;存储哪个节点处于活动状态可能会很有趣,因此您可以按照 Daniel 的回答中提出的 caretIn
和 caretOut
......这是一个简单的实现:
document.addEventListener('selectionchange', ev => {
var node = (document.getSelection().baseNode||0).parentNode,
old = document.lastGrab; if (node === old) return;
if (old) old.dispatchEvent(new Event('caretOut'));
(document.lastGrab = node).dispatchEvent(new Event('caretIn'));
});
var node = document.querySelector('span'); // find it the way you want
node.addEventListener('caretIn', function callback() { /* ... */ });
node.addEventListener('caretOut', function callback() { /* ... */ });
希望你现在有你需要的东西;快乐编码:)
答案 1 :(得分:1)
您可以使用MutationObserver。然后在观察者回调中检查characterData
的变化。
这里是一个示例:
var MutationObserver =
window.MutationObserver ||
window.WebKitMutationObserver ||
window.MozMutationObserver;
var config = { childList: true, characterData: true, characterDataOldValue:true, subtree: true };
var target = document
.querySelector('div[contenteditable]')
.getElementsByTagName('span')
.item(0);
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'characterData') {
console.clear();
console.log('old value:', mutation.oldValue);
console.log('new value:', mutation.target.textContent);
}
});
});
observer.observe(target, config);
<div contenteditable="true">some <span>spanned</span> text</div>
要检查插入符是否刚刚进入span
,应使用简单的click
侦听器来完成这项工作。
var target = document
.querySelector('div[contenteditable]')
.getElementsByTagName('span')
.item(0)
.addEventListener('click', function(evt) {
console.log('caret entered:', evt.target)
})
<div contenteditable="true">some <span>spanned</span> text</div>
答案 2 :(得分:0)
实际上,在考虑时-我真的找不到任何合适的本地事件来解决此问题。
问题是...插入符可以通过多种方式设置,单击,选择(可以在您要检测的区域开始或结束),移动光标的键盘事件甚至是外部脚本。
没有可以捕获所有事件的事件。
可悲的是,我相信这样做的唯一方法是循环并检查当前选择(保存插入符号数据)是否匹配良好。
为实现您的期望,我使用CustomElements实现了一个新元素,
实现主要是让您使用所需的任何语法-
是否为:myElement.dispatchEvent('caretin')
或仅使用html标签语法<caret-detector oncaretin="myFunction(event,'myArg')"></caret-detector>
。
我并不是说这是最好的主意,也不是当前想法的最佳实现,但我认为它涵盖了使用一个循环和本机js / html方法的所有情况。
//Create Appropriate Events And initialize them
var CaretInEvent = document.createEvent('Event'),
CaretOutEvent = document.createEvent('Event');
CaretInEvent.initEvent('caretin',true,true);
CaretOutEvent.initEvent('caretout',true,true);
//Next section will provide the caret handling functionallity
const CaretDetectorTagName = "caret-detector";
class CaretDetector extends HTMLElement {
//these will be used in order to support switching element in focus
static get lastDetected(){
return CaretDetector._lastDetected;
}
static set lastDetected(element){
CaretDetector._lastDetected = element;
}
//the interval
static detectorStart(){
setInterval(function(){
let focusNode = document.getSelection().focusNode;
if(focusNode){
focusNode = focusNode.parentNode.closest(CaretDetectorTagName) || focusNode.parentNode;
if(CaretDetector.lastDetected && focusNode !== CaretDetector.lastDetected && CaretDetector.lastDetected.inStatus === true && CaretDetector.lastDetected.triggerCaretOut){
CaretDetector.lastDetected.dispatchEvent(CaretOutEvent);
}
if(focusNode.triggerCaretIn && focusNode.inStatus !== true){
focusNode.dispatchEvent(CaretInEvent);
CaretDetector.lastDetected = focusNode;
}
}
},100);
}
//start with listening to the mentioned events
constructor(){
super();
this._instatus = false;
this.addEventListener('caretin', (...args)=>{
this._instatus = true;
this.triggerCaretIn(...args);
});
this.addEventListener('caretout', (...args)=>{
this._instatus = false;
this.triggerCaretOut(...args);
});
}
get inStatus(){
return this._instatus;
}
triggerCaretIn(event){
let desiredFn = this.getAttribute('oncaretin'),
wrapperFunction = new Function('event',`${desiredFn};`);
wrapperFunction(event);
}
triggerCaretOut(event){
let desiredFn = this.getAttribute('oncaretout'),
wrapperFunction = new Function('event',`${desiredFn};`);
wrapperFunction(event);
}
}
//actually connect the tag-name with the above class
customElements.define(CaretDetectorTagName, CaretDetector);
//the functions you want to execute when caret is in or out
function inFn(event){
console.log(event.srcElement.id + ": in");
}
function outFn(event){
console.log(event.srcElement.id + ": out");
}
window.onload = CaretDetector.detectorStart;
<div contenteditable="true">
012345
<caret-detector id="object1" oncaretin="inFn(event)" oncaretout="outFn(event)">STRING OF OBJECT 1</caret-detector>
<caret-detector id="object2" oncaretin="inFn(event)" oncaretout="outFn(event)">STRING OF OBJECT 2</caret-detector>
</div>