Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace selected text in Firefox

Tags:

javascript

How can I replace the selected text with another text using PURE javascript, in Firefox?

This I use to get the selection:

var sel = this.getSelection();
var range = sel.getRangeAt(0);

And also this important issue:
I want to keep the original format of characters (of course the new string will have the right format )
The selection can be done "cross-elements" (by this I mean that the selection can contain some text from one element like div or table and some text from another elements).

example, the document:

<div>
 this is a test
</div>
<div>
<b>still a test</b>
</div>
 <table style="width:100%;">
        <tr>
            <td>
                another word</td>
            <td>
                stackoverflow</td>
        </tr>
        <tr>
            <td>
                bump</td>
            <td>
               </td>
        </tr>
    </table>

the user select the following text (via one selection):

his is a test still a test anot

So now I want to replace the text keeping the format, for instance replace every thing with new string =

XXX XX X XXXX XXXXX X XXXX XXXX

Final document (after replace) will be:

<div>
 tXXX XX X XXXX
</div>
<div>
<b>XXXXX X XXXX</b>
</div>
 <table style="width:100%;">
        <tr>
            <td>
                XXXXher word</td>
            <td>
                stackoverflow</td>
        </tr>
        <tr>
            <td>
                bump</td>
            <td>
               </td>
        </tr>
    </table>
like image 282
kenny Avatar asked Apr 25 '26 08:04

kenny


1 Answers

Wooove, that was a pitty!

Javascript

var sel, range, nodevalue, startFound, stop;

function goThroughElements(el){
    // If el is the start node, set startFound to true
    if(el.isSameNode(range.startContainer)) startFound = true;
    
    if(startFound){
        // If this is the start node, replace the text like this: abcd[ef gh] --> abcdxx xx
        if(el.isSameNode(range.startContainer)){
            // \w stands for a word character
            nodevalue = el.nodeValue.substring(range.startOffset).replace(/\w/g, 'x');
            el.nodeValue = el.nodeValue.substring(0, range.startOffset) + nodevalue;
            
        }

        // If this is the end node, replace the value like this: [abc def]gh ij -> xxx xxxgh ij
        else if(el.isSameNode(range.endContainer)){
            nodevalue = el.nodeValue.substring(0,range.endOffset).replace(/\w/g, 'x');
            el.nodeValue = nodevalue + el.nodeValue.substring(range.endOffset);
            
            // Now we can stop
            stop = true;
        }
        
        // If this is just a text node, replace the value by xxxx
        else if(el.nodeType == 3){
            el.nodeValue = el.nodeValue.replace(/\w/g, 'x')
        }
    }
    
    // Loop trough el's brothers
    while(el){
        // Stop if we need to
        if(stop) return;
        
        // If this element has child nodes, call this function again with the first child node
        if(el.hasChildNodes()){
            goThroughElements(el.childNodes[0]);
        }
        
        // Jump to el's brother, or quit the loop
        if(el.nextSibling)
            el = el.nextSibling;
        else
            break;
    }
    
}
    
$(document).ready(function() {
    $(this).mouseup(function(){
        // Get the selection
        sel = window.getSelection();
        range = sel.getRangeAt(0);
        
        // Stop must be false if the last selected text node isn't found, startFound must be false when the start isn't found
        stop = false; startFound = false;
        
        if(range.collapsed == false){
            // Check if the selection takes place inside one text node element
            if(range.startContainer.isSameNode(range.endContainer)){
                // ab[cdefg]h  -> aaxxxxxh
                nodevalue = range.startContainer.nodeValue;
                range.startContainer.nodeValue = nodevalue.substring(0, range.startOffset) + nodevalue.substring(range.startOffset, range.endOffset).replace(/\w/g, 'x') + nodevalue.substring(range.endOffset);
            } else {    
                // If the start node of the selection isn't the same as the end node, loop through all elements
                goThroughElements(range.commonAncestorContainer.childNodes[0]);
            }
            // Collapse selection.
            range.collapse(true);
        }            
    });
});

Example

You can try the code of course

Maybe it's not the optimal solution, since it starts searching for the start node from the root. It would be faster to search from the first common parent element of range.startContainer and range.endContainer, but I don't know how to do that...

Edit

I wrapped the to-X functions inside if(range.collapsed == false) and used range.commonAncestorContainer.childNodes[0] in order to start iterating through the elements from the first child of the common parent of the start and end position of the selection

like image 175
Harmen Avatar answered Apr 27 '26 22:04

Harmen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!