简体   繁体   English

关于contenteditable = true元素的文本建议

[英]Text suggestions on a contenteditable=true element

I am using bootstrap-wysihtml5 (that is based on wysihtml5 ) amazing editor and it works fine and everyone is happy. 我正在使用bootstrap-wysihtml5 (基于wysihtml5 )惊人的编辑器,它工作正常,每个人都很高兴。 But now I need to offer text suggestions when the user is typing something (like this or this ). 但是现在我需要在用户输入内容(例如thisthis )时提供文本建议。 Those libraries expect a textarea and wysihtml5 uses an iframe with a <body contenteditable="true"> . 这些库希望使用textarea,而wysihtml5使用带有<body contenteditable="true">的iframe。

All I need is some words to auto-complete (or open a popup with the suggestions) in some plain text inside the element. 我所需要做的只是在元素内的一些纯文本中自动填写(或打开带有建议的弹出窗口)的单词。
Before I go deep in this, anyone has a suggestion of an library that could work inside an contenteditable="true" element? 在我深入探讨这个问题之前,没有人建议过可以在contenteditable="true"元素中使用的库吗?

Edit 1: 编辑1:
I created a basic lib that do what I need but I think I used all my poor js skills on this one... it works on a div with contenteditable=true but I'm having a hard time making it work on the wysihtml5 editor. 我创建了一个基本的库来满足我的需要,但是我想我在这上面使用了我所有的差劲的js技能...它可以在具有contenteditable=true的div上工作,但是我很难让它在wysihtml5编辑器上工作。 Can some javascript/wysihtml5/rangy ninja give me some help? 可以使用javascript / wysihtml5 / rangy忍者给我一些帮助吗?

Here is my code: http://jsfiddle.net/5UQfH/ 这是我的代码: http : //jsfiddle.net/5UQfH/

Edit 2: 编辑2:
First working version: http://jsfiddle.net/X9jBM/1/ 第一个工作版本: http : //jsfiddle.net/X9jBM/1/

Edit 3: 编辑3:
slightly better (but not prettier) version (works with multiple editors on same page): http://jsfiddle.net/X9jBM/18/ 稍好(但不美观)的版本(可在同一页面上使用多个编辑器): http : //jsfiddle.net/X9jBM/18/
Still do not work fine when the suggestion are multiple words (it stops suggesting when there is a space) 当建议为多个单词时仍然无法正常工作(如果有空格,它将停止提示)

Would still like to hear some feedback on this. 仍然希望听到对此的一些反馈。

I ended up creating a very simple and basic lib to do what I needed. 我最终创建了一个非常简单且基本的库来执行所需的操作。 It is not perfect nor tested in anything except the latest chrome version and I probably could eliminate the jQuery dependency quite easilly but since I already have it on my project (the old excuse), for now I will leave it as it is. 除了最新的chrome版本,它不是完美的,也没有经过任何测试的,我可能可以很轻松地消除jQuery依赖性,但是由于我已经在项目中使用了jQuery(旧的借口),现在我将保持现状。
ENTER select a word and TAB cycle through the words. ENTER选择一个单词,然后在单词之间进行TAB循环。 http://jsfiddle.net/X9jBM/19/ http://jsfiddle.net/X9jBM/19/

The code: 编码:

if (typeof String.prototype.startsWith != 'function') {
    String.prototype.startsWith = function (str) {
        return this.indexOf(str) == 0;
    };
}

var SuggestMe = function () {
"use strict";

var self = this;

return {
    init: init
};

function init(iframe, words) {

    self.list = [];
    self.currentIndex = 0;
    self.currentWord = "";
    self.$iframe = iframe;
    self.$editor = $(iframe).contents().find(".wysihtml5-editor");

    self.$editor.on("keydown", function (event) {

        if (event.keyCode === 13) {
            var sel = rangy.getIframeSelection(self.$iframe);

            if (!sel.isCollapsed) {
                var range = sel.getRangeAt(0);
                range.collapse(false);
                var textNode = document.createTextNode("  ");
                range.insertNode(textNode);
                sel.collapseToEnd();
                event.preventDefault();
                return false;
            }
        }

        if (event.keyCode === 9) {
            var sel = rangy.getIframeSelection(self.$iframe);
            if (!sel.isCollapsed) {
                self.currentIndex++;
                var word = self.list[self.currentIndex % self.list.length];
                var sel = rangy.getIframeSelection(self.$iframe);
                var range = sel.getRangeAt(0);
                range.deleteContents();
                var term = word.slice(self.currentWord.length, word.length);
                var textNode = document.createTextNode(term);
                range.insertNode(textNode);
                range.selectNodeContents(textNode);
                rangy.getSelection().setSingleRange(range);

                event.preventDefault();
                return false;
            }
        }

    });

    self.$editor.on("keyup", function (event) {

        var c = String.fromCharCode(event.keyCode);
        var isWordcharacter = c.match(/\w/);

        if (isWordcharacter && !event.ctrlKey) {

            var $editor = this;
            self.currentWord = getLastWord($editor);

            if (self.currentWord) {
                var sel = rangy.getIframeSelection(self.$iframe);

                if (sel.rangeCount > 0) {
                    self.list = [];
                    self.currentIndex = 0;

                    $.each(words, function (a) {
                        var word = words[a].toLowerCase();

                        if (word.startsWith(self.currentWord.toLowerCase())) {
                            self.list.push(word);
                        }
                    });
                }

                if (self.list.length) {
                    var word = self.list[self.currentIndex];
                    var sel = rangy.getIframeSelection(self.$iframe);
                    var range = sel.getRangeAt(0);
                    var term = word.slice(self.currentWord.length, word.length);
                    var textNode = document.createTextNode(term);
                    range.insertNode(textNode);
                    range.selectNodeContents(textNode);
                    rangy.getSelection().setSingleRange(range);
                }
            }
        }
    });
}


function getLastWord(elm) {
    var val = elm.innerText.trim();
    val = val.replace(/(\r\n|\n|\r)/gm, " ");
    var idx = val.lastIndexOf(' ');
    var lastWord = val.substring(idx + 1).trim();
    console.log(val);
    return lastWord;
}

};

The usage: 用法:

var suggestions = ["hello", "world", "blue dog", "blue cat"];

$('#txt').wysihtml5();
var editor = $('#txt').data("wysihtml5").editor;

editor.on('load', function () {
    var sm = new SuggestMe();
    sm.init(this.currentView.iframe, suggestions);
});

There is some refactor to be done but this is the basic idea. 需要进行一些重构,但这是基本思想。

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

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