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

Javascript 当插入符号进入特定的div/span/a标记时以及当插入符号离开标记时触发事件,javascript,jquery,angularjs,frontend,jquery-events,Javascript,Jquery,Angularjs,Frontend,Jquery Events,这个想法是- 有一个contenteditable元素,其中包含一些文本。我正在尝试建立一种标记机制(有点像推特的人在你输入@时标记)。每当用户键入“@”,当他们继续键入时,它就会显示一个带有建议和过滤器的弹出窗口。在这之前,一切都很简单,我已经弄明白了。当/仅当插入符号位于包含标记的元素上方时,当我需要显示popover时,就会出现问题 <div contenteditable=""> <p>Some random text before

这个想法是-
有一个
contenteditable
元素,其中包含一些文本。我正在尝试建立一种标记机制(有点像推特的人在你输入@时标记)。每当用户键入“@”,当他们继续键入时,它就会显示一个带有建议和过滤器的弹出窗口。在这之前,一切都很简单,我已经弄明白了。当/仅当插入符号位于包含标记的元素上方时,当我需要显示popover时,就会出现问题

<div contenteditable="">
  <p>Some random text before
    <a href="javascript:;"
       class="name-suggest"
       style="color:inherit !important;text-decoration:inherit !important">@samadams</a>
    Some random text after</p>
</div>

之前的一些随机文本
后面有一些随机文本

现在,每当用户将插入符号移动到a标记上/单击它时,我想触发一个显示popover的事件,并在插入符号离开a标记时将其删除。(有点像聚焦/模糊,但它们似乎不起作用)
onmousedown
起作用,但无法判断光标是否已用键盘移动到锚定标记中

此外,我在angularjs中这样做,因此,任何针对这一点的解决方案都是可取的,但不是必需的


一天来,我们一直在努力使其发挥作用,非常感谢您的帮助。

这将让您知道插入符号位置何时位于包含
@

$('#content')。在('mouseup keydown keydup',函数(事件){
var sel=getSelection();
如果(选择类型==“插入符号”){
var anchorNodeVal=sel.anchorNode.nodeValue;
if(anchorNodeVal.indexOf('@')>=0){
$('pop').show()
}否则{
$('#pop').hide()
}
}
})

之前的一些随机文本
后面有一些随机文本


Twitter节点发现
下面的代码中有一个奇怪的regexp和偏移量计算,但让我解释一下为什么它是一个更好的解决方案

大约一年前,我一直在使用contenteditable构建一个复杂的编辑器。这不仅仅是一场灾难。那是一场他妈的灾难。没有覆盖所有案例的规范。浏览器在每一个可能的细节上表现不同,并且经常变化。在
@
字符前面加上插入符号,您将看到这是壁虎:

<a href="#">|@name
而不是

$el = $('#content');

var showTip = function (nickname) {
    // ...
    console.log('Show: ' + nickname);
};

var dismissTip = function () {
    // ...
    console.log('Hide');
};

// I'm sure there is a better RegExp for this :)
var nicknameRegexp = /(^|\b|\s)\@(\w+)(\s|\b|$)/g;

var trackSelection = function () {
    var selection = window.getSelection(),
        range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;

    if (range == null || $el[0].contains(range.commonAncestorContainer) == false) {
        return dismissTip();
    }

    var comparer = range.cloneRange();
    comparer.setStart($el[0], 0);

    var offset = comparer.toString().length;

    var match, from, to;
    while (match = nicknameRegexp.exec($el[0].textContent)) {
        from = match.index + match[1].length;
        to = match.index + match[1].length + match[2].length + 1;
        if (offset >= from && offset <= to) {
            // Force rewind, otherwise next time result might be incorrect
            nicknameRegexp.lastIndex = 0;
            return showTip(match[2]);
        }
    }

    return dismissTip();
};

$el.on({
    // `mousedown` can happen outside #content
    'mousedown': function (e) {
        $(document).one('mouseup', function (e) {
            // Calling function without a tiny delay will lead to a wrong selection info
            setTimeout(trackSelection, 5);
        });
    },
    'keyup': trackSelection
});
$el=$(“#内容”);
var showTip=函数(昵称){
// ...
log('Show:'+昵称);
};
var dismissTip=函数(){
// ...
console.log('Hide');
};
//我相信有一个更好的RegExp用于此:)
变量昵称regexp=/(^ |\b |\s)\@(\w+)(\s |\b |$)/g;
var trackSelection=函数(){
var selection=window.getSelection(),
range=selection.rangeCount>0?selection.getRangeAt(0):空;
if(range==null | |$el[0]。包含(range.commonAncestorContainer)==false){
返回dismissTip();
}
var comparer=range.cloneRange();
comparer.setStart($el[0],0);
var offset=comparer.toString().length;
var匹配,从,到;
而(match=昵称regexp.exec($el[0].textContent)){
from=match.index+match[1]。长度;
to=match.index+match[1]。长度+match[2]。长度+1;
如果(offset>=from&&offset刚刚看了我来这里的原因,假装你的情况非常相似,只是发现当前单词是否特别以
@
开头,以便模态显示

您需要的是一种方法,可以在移动或键入时获取我们正在使用的单词,然后检查第一个字符并相应地隐藏/显示模式窗格将非常简单

function getSelectedWord(grab=document.getSelection()) {
    var i = grab.focusOffset, node = grab.focusNode, // find cursor
        text = node.data || node.innerText, // get focus-node text
        a = text.substr(0, i), p = text.substr(i); // split on caret
return  a.split(/\s/).pop() +  p.split(/\s/)[0]} // cut-out at spaces
现在,您可以收听
keydown
selectionchange
事件,并显示您的窗格,了解当前/所选单词已写入的内容

editor.addEventListener('keydown', ev => {
    if (ev.key.substr(0, 5) != 'Arrow') // react when we move caret or
    if (ev.key != '@') return; // react when we type an '@' or quit
    
    var word = getSelectedWord(); //  <-- checking value
    if (word[0] == '@') showModal(word.substr(1)); // pass without '@'
});
editor.addEventListener('keydown',ev=>{
if(ev.key.substr(0,5)!=“箭头”)//移动插入符号或
if(ev.key!='@')return;//在键入'@'或退出时做出反应

var word=getSelectedWord();//很酷。谢谢。这很有效,为实现我需要的东西提供了一些好主意。
Selection.type
不是在Gecko中实现的。很好的一点。您可以实际删除if块,它的功能很好,性能差异可能可以忽略
editor.addEventListener('keydown', ev => {
    if (ev.key.substr(0, 5) != 'Arrow') // react when we move caret or
    if (ev.key != '@') return; // react when we type an '@' or quit
    
    var word = getSelectedWord(); //  <-- checking value
    if (word[0] == '@') showModal(word.substr(1)); // pass without '@'
});