【问题标题】:Firing an event when the caret gets within a particular div/span/a tag and also, when the caret leaves the tag当插入符号进入特定的 div/span/a 标签以及插入符号离开标签时触发事件
【发布时间】:2015-01-16 06:11:09
【问题描述】:

想法是这样的 -
有一个 contenteditable 元素,其中包含一些文本。我正在尝试建立一个标记机制(有点像 twitter 的人在你输入“@”时标记)。每当用户输入“@”时,当他们继续输入时,它就会显示一个带有建议和过滤器的弹出框。直到这里这很容易,我已经弄清楚了。当/仅当插入符号位于包含标签的元素上方时,我需要显示弹出框时,问题就来了。

<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 标签上/单击它时,我想触发一个显示弹出框的事件,并在插入符号离开 a 标签时将其删除。 (有点像焦点/模糊,但它们似乎不起作用)。 onmousedown 有效,但无法判断光标是否已通过键盘移动到锚标记中。

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

已经尝试让它工作一天,非常感谢任何帮助。

【问题讨论】:

    标签: javascript jquery angularjs frontend jquery-events


    【解决方案1】:

    这将让您知道您的插入符号位置何时位于包含 @ 的锚节点中

    $('#content').on('mouseup keydown keyup', function (event) {
    
      var sel = getSelection();
      
      if (sel.type === "Caret") {
        var anchorNodeVal = sel.anchorNode.nodeValue;
        if ( anchorNodeVal.indexOf('@') >= 0) {
          $('#pop').show()
        } else {
          $('#pop').hide()
        }
      }
      
    })
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div id="content" 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>
      
    <div id="pop" style="display:none">Twitter node found</div>

    您可以添加一些正则表达式来进一步验证选择。

    【讨论】:

    • 酷。谢谢。这很有效,并提供了一些好主意来实现我需要的东西。
    • Selection.type 未在 Gecko 中实现。
    • 好点。您实际上可以删除 if 块并且它运行良好,性能差异可能可以忽略不计
    【解决方案2】:

    在下面的代码中,RegExps 和偏移量计算有一个奇怪的举动,但让我解释一下为什么它是一个更好的解决方案。

    大约一年前,我一直在使用 contenteditable 构建一个复杂的编辑器。这不仅仅是一场灾难。这是一场他妈的灾难。没有涵盖所有情况的规范。浏览器在每个可能的细节上都有不同的表现,并且经常变化。在@ char 之前添加一个插入符号,你会得到这是 Gecko:

    <a href="#">|@name
    

    这在 WebKit 中:

    |<a href="#">@name
    

    好吧,除非&lt;a&gt; 是段落的第一个孩子。那么结果将与 Gecko 中的结果相同。尝试将插入符号放在昵称之后,两者都会告诉它在链接内。开始输入,插入符号会弹出元素 - 一年前 Gecko 还没有这样做。

    我在这个例子中使用了原生的选择和范围 API,它们是 IE9+。您可能想改用Rangy

    $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
    });
    

    【讨论】:

      【解决方案3】:

      刚刚看了Fire event when caret enters span element,它把我带到了这里,假装你的情况很相似,除了发现当前单词是否专门以@开头以显示模态......

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

      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
      

      现在您可以收听keydownselectionchange 事件,并显示您的窗格,了解当前/所选单词的已写入内容。

      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 '@'
      });
      

      请注意,社交网络和代码完成通常在插入符号位置停止,而我确实检查了单词尾部...如果需要,您可以通过从 getSelectedWord 函数定义中删除 p 来照常进行。

      希望这仍然有帮助;快乐编码! ;)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-20
        • 1970-01-01
        • 2021-01-17
        • 1970-01-01
        相关资源
        最近更新 更多