contenteditable and non button elements

2019-03-20 23:06发布

问题:

I can easily do execcommand on a contenteditable selection if using a button. However using any other element fails.

http://jsbin.com/atike/edit

Why is this and how can I make it work using a div element.

Thanks.

回答1:

You could save the selection by using the mousedown event on the element being used instead of a button and restore it again after focussing the editable element in the click event.

I've modified the example code: http://jsbin.com/atike/40/edit. Here's the code:

function saveSelection() {
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            var ranges = [];
            for (var i = 0, len = sel.rangeCount; i < len; ++i) {
                ranges.push(sel.getRangeAt(i));
            }
            return ranges;
        }
    } else if (document.selection && document.selection.createRange) {
        return document.selection.createRange();
    }
    return null;
}

function restoreSelection(savedSel) {
    if (savedSel) {
        if (window.getSelection) {
            sel = window.getSelection();
            sel.removeAllRanges();
            for (var i = 0, len = savedSel.length; i < len; ++i) {
                sel.addRange(savedSel[i]);
            }
        } else if (document.selection && savedSel.select) {
            savedSel.select();
        }
    }
}

$(function() {
  var savedSel;

  $('.bold').mousedown(function () {
    savedSel = saveSelection();
  });

  $('.bold').click(function () {
    $('#hello').focus();
    if (savedSel) {
      restoreSelection(savedSel);
    }
    document.execCommand("bold", false, null);    
  });
});


回答2:

Most, if not all, of the WYSIWYG editors out there use an iframe element in order to not lose the selection. Another approach, although I haven't tried it, would be to store each selection made on that page after the mouseup event triggers.

Take a look at this page about Midas, Gecko's built-in rich text editor.



回答3:

What is happening is that when you click a text node, the browser wants to select that text. Buttons are not a part of the text flow and will therefore not trigger a new selection.

All you have to do is prevent the browser from performing a re-selection. When the click event you are currently observing runs the selection has already occurred and it's too late to act. The action starts to take place on mousedown for all browsers expect MSIE, which has a special selection events.

This works for me across browsers:

jQuery('.bold')
  .attr('unselectable','on') // prevents selection in MSIE
  .bind('mousedown',function (e) {
    document.execCommand("bold", false, null);
    e.preventDefault(); // prevents selection in all but MSIE
  });

There are a few complications with using MSIE's selection events (such as having to block dragging as well), so it's easier just to knock out the selectability of the element you are using as a button with the specialized attribute unselectable (needs the value "on").

You can obviously still perform the execCommand handling in click events and just run the selection prevention code on all elements intended as "buttons":

jQuery('.buttonlike')
  .attr('unselectable','on') // prevents selection in MSIE
  .bind('mousedown',function (e) {
    e.preventDefault(); // prevents selection in all but MSIE
  });

jQuery('#edit-bold').click(function(){
  document.execCommand("bold", false, null);
});


回答4:

NicEdit uses a combination of methods.

  • Most toolbar elements have the "unselectable" attribute set - this works for Internet Explorer.

  • On the same elements, it registers for the "mousedown" event and overrides the default action - this prevents both text solution and focus. This is for browsers other than IE only.

  • Some toolbar elements need to be able to receive text focus and make selections, such as the field for inserting a link or image. For these, it saves the selection before the editor loses focus, then restores it after the user has finished editing and the changes need to be made.

    For how to implement it, check NicEdit's code. Search for the function names:

    • getSel
    • getRng
    • selRng
    • saveRng
    • restoreRng

    ...

    This uses the browser's getSelection() or document.selection to get the selection, getRangeAt() to convert it to a range, and addRange() or select() to restore that range to as a selection.