2

Does anybody know how to use range.setStart in the same way as range.moveStart works in IE? I'd like to implement backspace/delete in JS, something like this:

range.moveStart('character',-1); range.deleteContents();

but in Firefox

Jaro
  • 21
  • 1
  • 2

2 Answers2

2

Firefox, along with all modern browsers except IE <= 8 uses DOM Ranges. There's no direct analogue to the moveStart method of IE's TextRange and it's tricky to do in the general case. If the range is within a text node and not at the start, it's easy; otherwise you'll need to walk backwards in the document to find the preceding text node and move the range into it. The following only works within a single text node:

function backspace() {
    var sel = window.getSelection();

    // If there is a selection rather than a caret, just delete the selection
    if (!sel.isCollapsed) {
        sel.deleteFromDocument();
    } else if (sel.rangeCount) {
        var range = sel.getRangeAt(0);
        if (range.startContainer.nodeType == 3 && range.startOffset > 0) {
            range.setStart(range.startContainer, range.startOffset - 1);
            sel.removeAllRanges();
            sel.addRange(range);
            sel.deleteFromDocument();
        }
    }
}

WebKit and Firefox 4 have the modify method of Selection objects which solves the problem completely:

function backspace2() {
    var sel = window.getSelection();

    // If there is a selection rather than a caret, just delete the selection
    if (!sel.isCollapsed) {
        sel.deleteFromDocument();
    } else if (sel.rangeCount && sel.modify) {
        sel.modify("extend", "backward", "character");
        sel.deleteFromDocument();
    }
}
Tim Down
  • 318,141
  • 75
  • 454
  • 536
0

Here’s a function to expand selection to cover full words:

document.body.addEventListener('keydown', ({key}) => {
    if (key === 'Enter') {
        getWordRange();
    }
});

function getWordRange() {
    const range = document.getSelection().getRangeAt(0);
    const {startContainer, startOffset, endContainer, endOffset} = range;
    const treeWalker = document.createTreeWalker(
        document.body,
        NodeFilter.SHOW_TEXT,
    );

    treeWalker.currentNode = startContainer;

    do {
        const container = treeWalker.currentNode;
        const content = container === startContainer
            ? container.textContent.substr(0, startOffset)
            : container.textContent;
        const offset = content.lastIndexOf(' ') + 1;

        range.setStart(container, 0);

        if (offset) {
            range.setStart(container, offset);
            break;
        }
    } while (treeWalker.previousNode());

    treeWalker.currentNode = endContainer;

    do {
        const container = treeWalker.currentNode;
        const content = container === endContainer
            ? container.textContent.substr(endOffset)
            : container.textContent;
        const offset = content.indexOf(' ');
        const actualOffset = offset + container.textContent.length - content.length;

        range.setEnd(container, content.length);

        if (offset !== -1) {
            range.setEnd(container, actualOffset);
            break;
        }
    } while (treeWalker.nextNode());
}
<p>
  Select text then hit Enter to expand selection to word edges.<br>
  Works with <b>nested <i>tags</i></b> as well.
</p>
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
waterplea
  • 3,462
  • 5
  • 31
  • 47
  • It seems, you have a small bug: instead of `range.setEnd(container, content.length);` it should be `range.setEnd(container, container.textContent.length);`. – 4esn0k Apr 18 '20 at 23:05