繁体   English   中英

当插入符在 contenteditable div 中输入 span 元素时触发事件

[英]Fire event when caret enters span element in contenteditable div

我有一个内容可编辑的 div,其跨度如下所示:

<div contenteditable="true">some <span>spanned</span> text</div>

我想知道是否有任何事件侦听器可以附加到span元素本身,可用于检测插入符号是否在span元素内移动。

我不是在寻找一个答案,在那里有听众附加到div ,每次在div有活动时运行检查,例如这个答案的解决方案:

当插入符号进入特定的 div/span/a 标签时以及当插入符号离开标签时触发事件

您可以使用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>

了解用户输入位置的最简单方法是document.getSelection() ,它提供了一些节点引用,如baseNode和当前focusNode以及有用的偏移信息。

然后,您可以在document上使用selectionchange事件来了解光标何时四处移动,而无需检查鼠标是否正在移动/单击或使用键盘事件。

 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 中的插入符号移动,以了解使用此方法是否超出您的范围; 存储哪个节点处于活动状态可能会很有趣,因此您可以caretIn Daniel 的回答中的建议使用caretIncaretOut ......这是一个简单的实现:

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() { /* ... */ });

希望你现在有你需要的东西; 快乐编码:)

实际上,在考虑它时 - 我真的找不到任何合适的本地事件来解决这个问题。

问题是...插入符号可以通过多种方式设置,单击,选择(可以在您希望检测的区域开始或结束),移动光标的键盘事件甚至外部脚本。

没有任何事件可以将它们全部捕获。

可悲的是,我相信这样做的唯一方法是循环并检查当前选择(包含插入符号数据)是否匹配良好。

为了实现您想要的功能,我使用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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM