【问题标题】:Javascript: How to move the caret to the inside of an inserted elementJavascript:如何将插入符号移动到插入元素的内部
【发布时间】:2014-06-15 15:04:51
【问题描述】:

我有一个内部构建的 WYSIWYG 编辑器,它能够使用 JavaScript 范围和选择对象选择内容并将其转换为列表(它也可以执行其他操作)。这类似于在插入 BR 标记时将插入符号移动到行尾(我使用 Tim Down 的示例,提供 here)来实现这一点,但是我需要将光标保持在初始节点内,以便更多节点可以插入。初始节点将是 OL | UL 标记,我需要能够插入带有所选文本的列表项。

我用来处理这个的代码:

OverrideListCreation: function (commandName, showDefaultUI, commandValue, context) {
        var html;
        var contextHtml = context.range.htmlText;
        var insertSuccess;
        var element;

        console.log("ContextHtml: " + contextHtml);

        if (commandName == "insertorderedlist") {
            element = this.designModeDocument.createElement("ol");
        } else {
            element = this.designModeDocument.createElement("ul");
        }

        element.setAttribute("class", "content");
        element.setAttribute("style", "margin: 1em;");

        insertSuccess = this.InsertNodeAtCursor(context, element, false);      

        element = this.designModeDocument.createElement("li");
        element.setAttribute("class", "content");
        element.setAttribute("style", "margin: 0em;");

        if (contextHtml.search(/<br>/gi) > -1) {
            console.log("ContextHtml Length: " + contextHtml.length);
            console.log("Multiline Check - Passed");
            var lines = contextHtml.split(/<br>/gi);

            for (var i = 0; i < lines.length; i++) {
                element.innerHTML = lines[i];

                insertSuccess = this.InsertNodeAtCursor(context, element, false);

                if (insertSuccess === false) {
                    return;
                }
            }
        } else if (contextHtml.length > 1 && contextHtml.search(/<br>/gi) === -1) {
            console.log("Single Line Check w/o BR - Passed");
            element.innerHTML = contextHtml;

            insertSuccess = this.InsertNodeAtCursor(context, element, false);

            if (insertSuccess === false) {
                return;
            }
        } else {
            console.log("ContextHtml is empty, insert an empty list element");
            insertSuccess = this.InsertNodeAtCursor(context, element, false);

            if (insertSuccess === false) {
                return;
            }
        }

        return;

供参考:上下文对象代码:

GetDesignModeContext: function () {
        var context = new Object();

        try {
            if (this.designModeDocument.selection) {
                context.selection = this.designModeDocument.selection;
                context.range = context.selection.createRange();
                context.selectedText = context.range.text;

                switch (context.selection.type) {
                    case "None":
                    case "Text":
                        context.parentElement = context.range.parentElement();
                        break;
                    case "Control":
                        context.parentElement = context.range.item(0);
                        break;
                    default:
                        context.parentElement = this.designModeDocument.body;
                        break;
                }
            }
            else if (this.designModeDocument.getSelection || this.designEditor.getSelection) {
                context.selection = this.designEditor.getSelection();
                context.selectedText = context.selection.toString();

                try {
                    context.range = context.selection.getRangeAt(0);
                }
                catch (e) {
                    context.range = this.designModeDocument.createRange();
                }

                function IsSelectedTextNode(container, offset, start) {
                    if (container.nodeType != 3) return false;
                    var startIndex = start ? offset : 0;
                    var endIndex = start ? container.nodeValue.length : offset + 1;
                    var text = container.nodeValue.substring(startIndex, endIndex);

                    return (context.selectedText == text);
                }

                var r = context.range;
                var p = null;

                if (r.startContainer == r.endContainer) {
                    if (r.collapsed) {
                        p = r.startContainer;
                    }
                    else if (r.startOffset - r.endOffset <= 1 &&
                             r.startContainer.hasChildNodes()) {
                        p = r.startContainer.childNodes[r.startOffset];
                    }
                }
                else if (IsSelectedTextNode(r.startContainer, r.startOffset, true)) {
                    p = r.startContainer;
                }
                else if (IsSelectedTextNode(r.endContainer, r.endOffset, false)) {
                    p = r.endContainer;
                }

                if (!p) p = r.commonAncestorContainer;

                while (p.nodeType == 3) p = p.parentNode;

                context.parentElement = p;
            }

            if (context.parentElement == null) return null;
            if (context.parentElement.nodeType != 1) return null;
            if (context.parentElement.ownerDocument != this.designModeDocument) return null;
        }
        catch (e) {
            return null;
        }


        return context;
    },

光标代码处的插入节点:

InsertNodeAtCursor: function (context, domElement, positionEnd) {
        var selection, range;

        if (typeof this.designModeDocument.getSelection != "undefined" && context.selection) {
            //console.log("getSelection is supported by this browser && Context.Selection was not undefined.");
            selection = this.designModeDocument.getSelection() || context.selection;

            if ((selection.getRangeAt && selection.rangeCount) && context.range) {
                //console.log("getRangeAt & rangeCount are supported by this browser && Context.Range is not undefined");

                range = selection.getRangeAt(0) || context.range;

                range.deleteContents();

                //console.log("Inserting BR Element Node");
                range.insertNode(domElement);

                //console.log("Setting Position of Cursor");
                if (positionEnd) {
                    range.setEndAfter(domElement);
                    range.setStartAfter(domElement);
                } else {

                }



                //console.log("Selection Clean-up");
                selection.removeAllRanges();
                selection.addRange(range);

                return true;
            }
        } else if (typeof this.designModeDocument.selection != "undefined" && context.selection) {
            //console.log("Using alternate selection method.");

            if (typeof selection.createRange && context.range) {
                //console.log("Using alternate range method.");
                range = selection.createRange || context.range;

                var tmpElement = this.designModeDocument.createElement("div");
                tmpElement.appendChild(domElement);

                this.PasteHtml(range, tmpElement.innerHTML);

                range.select();

                this.designModeDocument.removeChild(tmpElement);

                return true;
            }
        } else {
            return false;
        }
    },

我最初的想法是将 setStartAfter 和 setEndAfter 函数更改为不移动光标,但这不起作用,这就是为什么在这些函数后面有一个空的 else 块。

编辑:我确实有一个旧版本的列表创建代码,它将获取选定的文本并通过用开始和关闭列表标签替换断行来围绕它构建列表项,之后它将它们粘贴到可编辑的文档中(最初的目标是 IE。原始代码是作为包含的脚本文件编写的,这就是它具有不同结构的原因。

function doCommandIERangeList(strCommand, bShowDefaultUI, strOptional) {
    // Get a text range for the selection
    var strHTML;
    var strHTMLFromControl;
    var tr = getIFrameDocument(_strIFrame).selection.createRange();

    strHTMLFromControl = tr.htmlText;

    //if there are break lines present create a new list
    //otherwise we just want to switch between list types
    if (strHTMLFromControl.search(/<BR>/gi) > -1) {

        if (strCommand == "InsertOrderedList") {
            strHTML = "<ol>";
        } else {
            strHTML = "<ul>";
        }


        strHTML += "<li>" + strHTMLFromControl.replace(/<BR>/gi, "</li><li>") + "</li>";


        tr = getIFrameDocument(_strIFrame).selection.createRange();

        if (strCommand == "InsertOrderedList") {
            strHTML += "</ol>";
        } else {
            strHTML += "</ul>";
        }

        //IE Version Check to remove somewhat randomly placed <br> tags when trying to format a set of text instructions as a List Range
        if (IsIE9()) {
            strHTML = strHTML.replace(/[\r\n]/g, "");
        }

        if (IsIE10()) {
            strHTML = strHTML.replace(/[\r\n]/g, "");
        }

        tr.pasteHTML(strHTML);

    } else {
        //needed to switch between ul and ol
        tr.execCommand(strCommand, bShowDefaultUI, strOptional);
    }

    // Reselect and give the focus back to the editor
    tr.select();
    frames.editor.focus();
}

更新:我想出了如何构建 HTML(我将在下面发布代码)并插入到文档中,但是列表项在编辑器窗口中没有正确呈现。 有什么想法吗?

OverrideListCreation: function (commandName, showDefaultUI, commandValue, context) {
    var html;
    var contextHtml = context.range.htmlText;
    var insertSuccess;
    var parentElement,element;

    console.log("ContextHtml: " + contextHtml);

    if (commandName == "insertorderedlist") {
        parentElement = this.designModeDocument.createElement("ol");
        parentElement.setAttribute("type", "1");
    } else {
        parentElement = this.designModeDocument.createElement("ul");
        parentElement.setAttribute("type", "disc");
    }

    parentElement.setAttribute("class", "content");
    parentElement.setAttribute("style", "margin: 1em;");



    if (contextHtml.search(/<br>/gi) > -1) {
        console.log("ContextHtml Length: " + contextHtml.length);
        console.log("Multiline Check - Passed");
        var lines = contextHtml.split(/<br>/gi);

        for (var i = 0; i < lines.length; i++) {
            console.log("Lines @ " + i + ": " + lines[i]);

            element = this.designModeDocument.createElement("li");
            element.setAttribute("class", "content");
            element.setAttribute("style", "margin: 0em;");

            if (typeof element.innerHTML != "undefined") {
                element.innerHTML = lines[i]
            } else {
                element.textContent = lines[i];
            }

            parentElement.appendChild(element);

        }
    } else if (contextHtml.length > 1 && contextHtml.search(/<br>/gi) === -1) {
        console.log("Single Line Check w/o BR - Passed");

        element = this.designModeDocument.createElement("li");
        element.setAttribute("class", "content");
        element.setAttribute("style", "margin: 0em;");

        if (typeof element.innerHTML != "undefined") {
            element.innerHTML = contextHtml
        } else {
            element.textContent = contextHtml
        }

        parentElement.appendChild(element);

    } else {
        console.log("ContextHtml is empty, insert an empty list element");

        element = this.designModeDocument.createElement("li");
        element.setAttribute("class", "content");
        element.setAttribute("style", "margin: 0em;");

        parentElement.appendChild(element);
    }

    insertSuccess = this.InsertNodeAtCursor(context, parentElement, false);

    return;

指定类型属性(对 HTML 5 不友好)并没有解决我的问题,因此它们未被使用。

【问题讨论】:

    标签: javascript html html-lists wysiwyg


    【解决方案1】:

    我通过更新创建上下文对象的代码来从文档中获取选定的 HTML 来解决这个问题。从那里,我能够通过提供的 HTML 字符串操作 DOM,并在函数完成后重新发布到 DOM。 (另外,我意识到我最初的问题非常模糊,但我希望有一天我的代码 sn-ps 对某人有用)

    更新上下文函数:

    GetDesignModeContext: function () {
        var context = new Object();
    
        try {
            //console.log("Check Function Support: designModeDocument.getSelection && designEditor.getSelection")
            if (typeof this.designModeDocument.getSelection != "undefined" && typeof this.designEditor.getSelection != "undefined") {
    
                var editor = this;
                //console.log("Get: Selection Object")
                context.selection = this.designEditor.getSelection();
                //console.log("Get: Selection Text");
                context.selectedText = context.selection.toString();
                //console.log("Call Function: GetSelectedContextHtml");
                context.selectedHtml = GetSelectedContextHtml(editor);
    
                try {
                    //console.log("Get: Range Object");
                    context.range = context.selection.getRangeAt(0);
                    //context.parentElement = context.range.commonAncestorContainer;
                } catch (e) {
                    //console.log("Get: Range Object [LEGACY]");
                    context.range = this.designModeDocument.createRange();
                    //context.parentElement = context.range.parentElement();
                }
    
                function GetSelectedContextHtml(editor) {
                    //console.log("Start Function: GetSelectedContextHTML");
                    var html;
    
                    //console.log("Check Function Support: getSelection");
                    if (typeof editor.designEditor.getSelection != "undefined") {
    
                        var sel = editor.designEditor.getSelection();
    
                        if (sel.rangeCount) {
                            //console.log("Check Property Support: rangeCount");
                            var container = editor.designModeDocument.createElement("div");
    
                            for (var i = 0, len = sel.rangeCount; i < len; i++) {
                                //console.log("Get: Range Object HTML @ Index[" + i + "]");
                                container.appendChild(sel.getRangeAt(i).cloneContents());
                            }
    
                            //console.log("Set: HTML = Container.InnerHTML");
                            html = container.innerHTML;
                        }
                    } else if (typeof editor.designModeDocument.selection != "undefined") {
                        //console.log("Check Function Support: Selection");
                        if (editor.designModeDocument.selection.type == "Text") {
                            //console.log("Check Selection Type: Text");
                            html = editor.designModeDocument.selection.createRange().htmlText;
                        }
                    }
                    //console.log("End Function: GetSelectedContextHTML");
                    return html;
                }
    
                function IsSelectedTextNode(container, offset, start) {
                    if (container.nodeType != 3) return false;
    
                    var startIndex = start ? offset : 0;
                    var endIndex = start ? container.nodeValue.length : offset + 1;
                    var text = container.nodeValue.substring(startIndex, endIndex);
    
                    return (context.selectedText == text)
                }
    
    
    
                var r = context.range;
                var p = null;
    
    
                if (r.startContainer == r.endContainer) {
                    console.log("Check: Range.StartContainer == Range.EndContainer");
                    if (r.collapsed) {
                        console.log("Check: Range.Collapsed");
                        console.log("Set: Parent = Range.StartContainer");
                        p = r.startContainer;
                    } else if (r.startOffset - r.endOffset <= 1 && r.startContainer.hasChildNodes()) {
                        console.log("Check: Difference Between Offsets <= 1 && Range.StartContainer.HasChildNodes");
                        console.log("Set: Parent = Range.StartContainer.ChildNodes @ Index[" + r.startOffset + "]");
                        p = r.startContainer.childNodes[r.startOffset];
                    }
                } else if (IsSelectedTextNode(r.startContainer, r.startOffset, true)) {
                    console.log("Check Function: IsSelectedTextNode(range.startContainer, range.startOffset, start = true)");
                    console.log("Set: Parent = Range.StartContainer");
                    p = r.startContainer;
                } else if (IsSelectedTextNode(r.endContainer, r.endOffset, false)) {
                    console.log("Check Function: IsSelectedTextNode(range.endContainer, range.endOffset, start = false)");
                    console.log("Set: Parent = Range.EndContainer");
                    p = r.endContainer;
                }
    
                if (!p) {
                    console.log("Check: Parent != Nothing");
                    console.log("Set: Parent = Range.CommonAncestor");
                    p = r.commonAncestorContainer;
                }
    
                console.log("Loop: Parent.NodeType = 3");
                while (p.nodeType == 3) p = p.parentNode;
    
                console.log("Set: Context.ParentElement = Parent");
                context.parentElement = p;
    
                //console.log("Context.Selection: " + context.selection);
                //console.log("Context.SelectedText: " + context.selectedText);
                //console.log("Context.SelectedHtml: " + context.selectedHtml);
                //console.log("Context.ParentElement: " + context.parentElement);
                //console.log("Context.Range: " + context.range);
    
            } else if (typeof this.designModeDocument.selection != "undefined") {
                context.selection = this.designModeDocument.selection;
                context.range = context.selection.createRange();
                context.selectedText = context.range.text;
                context.selectedHtml = context.range.htmlText;
    
                switch (context.selection.type) {
                    case "None":
                    case "Text":
                        context.parentElement = context.range.parentElement();
                        break;
                    case "Control":
                        context.parentElement = context.range.item(0);
                        break;
                    default:
                        context.parentElement = this.designModeDocument.body;
                        break;
                }
            }
    
            if (context.parentElement == null) return null;
            if (context.parentElement.nodeType != 1) return null
            if (context.parentElement.ownerDocument != this.designModeDocument) return null;
    
        } catch (e) {
            return null;
        }
    
        return context;
    },
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-08
      • 2019-05-28
      • 1970-01-01
      相关资源
      最近更新 更多