11

I am using a text editor provided by Microsoft ajax-toolkit.
It renders iframe on browser. I have added a dropdown in that editor and I want that when user changes the drop-down index the value should be added in the editor current cursor position.

I got a code on SO which gives me the current selected text inside editor is as follows

function getIframeSelectionText(iframe) {
        var win = iframe.contentWindow;
        var doc = iframe.contentDocument || win.document;

        if (win.getSelection) {
            return win.getSelection().toString();

        } else if (doc.selection && doc.selection.createRange) {
            return doc.selection.createRange().text;
        }
 }

But I want to add some text at the current position. The html is rendering as below

<td class="ajax__htmleditor_editor_editpanel"><div id="Editor1_ctl02" style="height:100%;width:100%;">
            <iframe id="Editor1_ctl02_ctl00" name="Editor1_ctl02_ctl00" marginheight="0" marginwidth="0" frameborder="0" style="height:100%;width:100%;display:none;border-width:0px;">

            </iframe><textarea id="Editor1_ctl02_ctl01" class="ajax__htmleditor_htmlpanel_default" style="height:100%;width:100%;display:none;"></textarea><iframe id="Editor1_ctl02_ctl02" name="Editor1_ctl02_ctl02" marginheight="0" marginwidth="0" frameborder="0" style="height:100%;width:100%;display:none;border-width:0px;">

            </iframe>
        </div></td>

I am trying as follow

$("#imgDropdown").change(function () {
            //var iframeBody =    $(window.Editor1_ctl02_ctl00.document.getElementsByTagName("body")[0]);
            var iframe = document.getElementById("Editor1_ctl02_ctl00");
            $("#Editor1_ctl02_ctl00").find("body").insertAtCaret("value");
            //alert(getIframeSelectionText(iframe));
        });

the function for inserting text is not working with iframe is as follow

$.fn.extend({
        insertAtCaret: function (myValue) {
            if (document.selection) {
                this.focus();
                sel = document.selection.createRange();
                sel.text = myValue;
                this.focus();
            }
            else if (this.selectionStart || this.selectionStart == '0') {
                var startPos = this.selectionStart;
                var endPos = this.selectionEnd;
                var scrollTop = this.scrollTop;
                this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos, this.value.length);
                this.focus();
                this.selectionStart = startPos + myValue.length;
                this.selectionEnd = startPos + myValue.length;
                this.scrollTop = scrollTop;
            } else {
                this.value += myValue;
                this.focus();
            }
        }
    })
Cœur
  • 37,241
  • 25
  • 195
  • 267
शेखर
  • 17,412
  • 13
  • 61
  • 117

2 Answers2

8

Easy, you just have to use.

$("#Editor1_ctl02_ctl00").contents().find('textarea').insertAtCaret('value');

Updated

Sorry, I thought the insertAtCaret function is working for you, you just needed to work inside iFrame. You can use this version of insertAtCaret:

jQuery.fn.extend({
    insertAtCaret: function (html) {
        var winObject = function (el){
            var doc = el.ownerDocument;
            return doc.defaultView || doc.parentWindow
        };
        return this.each(function (i) {
                var sel, range, w = this;
                w = winObject(w);
                if (w.getSelection) {
                    // IE9 and non-IE
                    sel = w.getSelection();
                    if (sel.getRangeAt && sel.rangeCount) {
                        range = sel.getRangeAt(0);
                        range.deleteContents();

                        // Range.createContextualFragment() would be useful here but is
                        // only relatively recently standardized and is not supported in
                        // some browsers (IE9, for one)
                        var el = w.document.createElement("div");
                        el.innerHTML = html;
                        var frag = w.document.createDocumentFragment(), node, lastNode;
                        while ((node = el.firstChild)) {
                            lastNode = frag.appendChild(node);
                        }
                        range.insertNode(frag);

                        // Preserve the selection
                        if (lastNode) {
                            range = range.cloneRange();
                            range.setStartAfter(lastNode);
                            range.collapse(true);
                            sel.removeAllRanges();
                            sel.addRange(range);
                        }
                    }
                } else if (w.document.selection && w.document.selection.type != "Control") {
                    // IE < 9
                    w.document.selection.createRange().pasteHTML(html);
                }
            }
        )
    }
});

and call it like:

$("#Editor1_ctl02_ctl00").contents().find('body').insertAtCaret($val);

Function adapted from here

Happy coding!

Community
  • 1
  • 1
xiidea
  • 3,344
  • 20
  • 24
0

There seem to be a few issues here.

  1. The Microsoft ajax-toolkit editor creates an iframe where the designMode property is turned on, and that's why it's editable, it has no value, and textNodes are added straight to the body, which makes it a little more difficult.

  2. When you're selecting something from a dropdown, the focus is on the dropdown, and there is no caret position, as the focus is shifted away from the iFrame.
    I'm assuming that the dropdown is in the top menubar for the editor or anywhere else that is outside the iFrame.

Also, the Microsoft ajax-toolkit editor has a recommended update, the HTMLEditorExtender.

The code you have to capture the caret position seems to be for a regular input / textarea, and you'd have to adapt that code to work with any Node inside an iframe that is in designMode, with it's own window and document etc.

Given the above considerations, this is what I came up with to do this

var frameID  = 'Editor1_ctl02_ctl00',
    selectID = 'imgDropdown',
    iframe   = document.getElementById(frameID),
    iWin     = iframe.contentWindow ? iframe.contentWindow : window.frames[frameID];

$(iWin).on('blur', function() {
    $(iframe).data('range', getRange(iWin));
});

$('#' + selectID).on('change', function() {
    var range = $(iframe).data('range');
    addText(iWin, range, this.value);
});


function getRange(win) {
    var sel, range, html;
    if (win.getSelection) {
        sel = win.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();
        }
    } else if (win.document.selection && win.document.selection.createRange) {
        range = win.document.selection.createRange();
    }
    return range;
}

function addText(win, range, text) {
    if (win.getSelection) {
       range.insertNode(win.document.createTextNode(text));
    } else if (win.document.selection && win.document.selection.createRange) {
        range.text = text;
    }
}

FIDDLE

adeneo
  • 312,895
  • 29
  • 395
  • 388