【问题标题】:Rangy: word under caret (again)Rangy:插入符号下的单词(再次)
【发布时间】:2014-02-27 18:15:57
【问题描述】:

我正在尝试创建一个预先输入代码以添加到 wysihtml5 富文本编辑器。 基本上,我需要能够插入诸如 Twitter/Github/Facebook 之类的人物/标签引用...。

我发现一些人试图实现同样的目标。

http://jsfiddle.net/A9z3D/

这工作得很好,只是它只对最后一个单词做建议并且有一些错误。而且我想要一个像 Twitter 这样的选择框,而不是使用 Tab 键进行简单的“选择切换”。

为此,我尝试检测当前输入的单词。

        getCurrentlyTypedWord: function(e) {
            var iframe = this.$("iframe.wysihtml5-sandbox").get(0);
            var sel = rangy.getSelection(iframe);
            var word;
            if (sel.rangeCount > 0 && sel.isCollapsed) {
                console.debug("Rangy: ",sel);
                var initialCaretPositionRange = sel.getRangeAt(0);
                var rangeToExpand = initialCaretPositionRange.cloneRange();
                var newStartOffset = rangeToExpand.startOffset > 0 ? rangeToExpand.startOffset - 1 : 0;
                rangeToExpand.setStart(rangeToExpand.startContainer,newStartOffset);
                sel.setSingleRange(rangeToExpand);
                sel.expand("word", {
                    trim: true,
                    wordOptions: {
                         includeTrailingSpace: true,
                         //wordRegex: /([a-z0-9]+)*/gi
                         wordRegex: /[a-z0-9]+('[a-z0-9]+)*/gi
                        // wordRegex: /([a-z0-9]+)*/gi
                    }
                });
                word = sel.text();
                sel.removeAllRanges();
                sel.setSingleRange(initialCaretPositionRange);
            } else {
                word = "noRange";
            }
            console.debug("WORD=",word);
            return word;

这仅在选择折叠时触发。 请注意,我必须处理起始偏移量的向后移动,因为如果插入符号位于单词的末尾(就像大多数用户键入时的情况一样),那么扩展函数不会围绕当前输入的单词。

到目前为止,这工作得很好,问题是它使用了具有TextRangeModule 的 Rangy 1.3 的 alpha 版本。问题是我注意到 wysihtml5 也在不同且不兼容的版本 (1.2.2) 中使用 Rangy(rangy.dom 的问题可能已被删除)。

由于 Rangy 使用全局 window.rangy 变量,我想我还是必须使用 1.2.2 版本。

如何仅使用 rangy 1.2.2 来实现 expand 的等效功能?

编辑:顺便问一下,除了使用expand 函数,还有其他解决方案吗?我认为修改当前选择并将其恢复回来只是为了知道当前输入的单词有点奇怪和笨拙。没有不涉及选择当前键入的单词的解决方案吗?我的意思是,一旦我们知道初始插入符号折叠范围,就只基于范围?

【问题讨论】:

    标签: javascript dom rangy


    【解决方案1】:

    由于 Rangy 使用全局 window.rangy 变量,我想我还是必须使用 1.2.2 版本。

    在阅读了 Rangy 的代码后,我的直觉是在同一页面中加载两个版本的 Rangy 可能是可行的。我做了一个谷歌搜索,发现我是对的。 Tim Down(Rangy 的创建者)在issue report 中解释了它。他举了这个例子:

    <script type="text/javascript" src="/rangy-1.0.1/rangy-core.js"></script>
    <script type="text/javascript" src="/rangy-1.0.1/rangy-cssclassapplier.js"></script>
    
    <script type="text/javascript">
        var rangy1 = rangy;
    </script>
    
    <script type="text/javascript" src="/rangy-1.1.2/rangy-core.js"></script>
    <script type="text/javascript" src="/rangy-1.1.2/rangy-cssclassapplier.js"></script>
    

    因此,您可以加载 您的代码 需要的 Rangy 版本。重命名它并在您的代码中使用此名称,然后加载 wysihtml5 想要的并将 this 版本保留为 rangy

    否则,必须自己实现 expand 以忠实地复制 Rangy 1.3 所做的事情并不是一件简单的事情。

    这是一个极其原始的代码实现,它将选择扩展到单词边界。此代码将被单词中开始或结束的元素触发。

    var word_sep = " ";
    
    function expand() {
        var sel = rangy.getSelection();
        var range = sel.getRangeAt(0);
    
        var start_node = range.startContainer;
        if (start_node.nodeType === Node.TEXT_NODE) {
            var sep_at = start_node.nodeValue.lastIndexOf(word_sep, range.startOffset);
            range.setStart(start_node, (sep_at !== -1) ? sep_at + 1 : 0);
        }
    
        var end_node = range.endContainer;
        if (end_node.nodeType === Node.TEXT_NODE) {
            var sep_at = end_node.nodeValue.indexOf(word_sep, range.endOffset);
            range.setEnd(end_node, (sep_at !== -1) ? sep_at : range.endContainer.nodeValue.length);
        }
        sel.setSingleRange(range);
    }
    

    这是一个fiddle。这应该适用于范围广泛的 1.2.2。 (它甚至可以在没有范围的情况下工作。)

    【讨论】:

    • 谢谢,但是如果你使用 requirejs 怎么处理呢? :) 顺便说一句,我编辑了我的帖子,因为我认为也许可以使用 expand 以外的其他东西。 Expand 在我看来只是一个捷径,但可能不是最优雅的解决方案。
    • 只是您的代码使用 RequireJS 还是 wysihtml5 也使用它?至于使用expand 以外的其他东西,您必须指定您希望获得的确切功能,因为将“选择”扩展到整个单词 的想法是模棱两可的。考虑这种情况“这是一个example”。一个以“xa”开头的幼稚实现作为选择会将其扩展为“exam”并停在那里。但这不是我们人类理解概念的方式整个单词。我们希望选择“示例”。
    • 这个想法很简单:做一些类似于推特推文编辑器的事情:能够检测以@或#开头的单词,显示一个预先输入框,然后用@包围@Person或#hashtag 987654329@ 或href 或类似的东西。我想只有在开始/结束节点相同时才可以触发此行为所以我可能不需要扩展到examble 但只有exam 可以:我认为任何用户都不会尝试将主题标签的一部分加粗
    • 我添加了一个示例实现。非常原始,但也许就够了。
    • 感谢@Louis perfect :) 正在考虑这样的解决方案,但不太了解 dom/range apis
    【解决方案2】:

    对于那些感兴趣的人,根据@Louis 的建议,我制作了这个 JsFiddle,它显示了一个 wysihtml5 集成以了解当前输入的单词。

    它不需要使用仍然是 alpha 版本的 rangy 1.3 中的 expand 函数。

    http://jsfiddle.net/zPxSL/2/

    $(function () {
    
        $('#txt').wysihtml5();
    
        var editor = $('#txt').data("wysihtml5").editor;
    
        $(".wysihtml5-sandbox").contents().find("body").click(function(e) {
            getCurrentlyTypedWord();
        });
        $(".wysihtml5-sandbox").contents().find("body").keydown(function(e) {
            getCurrentlyTypedWord();
        });
    
        function getCurrentlyTypedWord() {
            var iframe = this.$("iframe.wysihtml5-sandbox").get(0);
            var sel = rangy.getIframeSelection(iframe);
            var wordSeparator = " ";
            if (sel.rangeCount > 0) {
                var selectedRange = sel.getRangeAt(0);
                var isCollapsed = selectedRange.collapsed;
                var isTextNode = (selectedRange.startContainer.nodeType === Node.TEXT_NODE);
                var isSimpleCaret = (selectedRange.startOffset === selectedRange.endOffset);
                var isSimpleCaretOnTextNode = (isCollapsed && isTextNode && isSimpleCaret);
                // only trigger this behavior when the selection is collapsed on a text node container,
                // and there is an empty selection (this means just a caret)
                // this is definitely the case when an user is typing
                if (isSimpleCaretOnTextNode) {
                    var textNode = selectedRange.startContainer;
                    var text = textNode.nodeValue;
                    var caretIndex = selectedRange.startOffset;
                    // Get word begin boundary
                    var startSeparatorIndex = text.lastIndexOf(wordSeparator, caretIndex);
                    var startWordIndex = (startSeparatorIndex !== -1) ? startSeparatorIndex + 1 : 0;
                    // Get word end boundary
                    var endSeparatorIndex = text.indexOf(wordSeparator, caretIndex);
                    var endWordIndex = (endSeparatorIndex !== -1) ? endSeparatorIndex : text.length
                    // Create word range
                    var wordRange = selectedRange.cloneRange();
                    wordRange.setStart(textNode, startWordIndex);
                    wordRange.setEnd(textNode, endWordIndex);
                    console.debug("Word range:", wordRange.toString());
                    return wordRange;
                }
            }
        }
    
    
    });
    

    【讨论】:

      猜你喜欢
      • 2011-10-12
      • 1970-01-01
      • 1970-01-01
      • 2012-01-19
      • 1970-01-01
      • 1970-01-01
      • 2019-12-13
      • 1970-01-01
      • 2020-10-31
      相关资源
      最近更新 更多