4

I need to run a search and replace on HTML similar to the following... I need to have "Find Next" "Replace" and "Replace All" options.... the trick is that I need to run an AJAX request to update the values in the database for each field once the value is replaced.

The only trouble I'm running into is that I"m unsure exactly how to search the contents of #sheet and replace the values with the new value the user has provided.

<div id='sheet'>
<div class='item' id='field-18583'>This is yet another test</div>
<div class='item' id='field-18585'>This is test data</div>
</div>

I should say that it's possible I'll have TONS of text to search, so ideally I'd only find the next instance of the item that's being searched for, not all instances. So when I hit "Find Next", if I"m 3 items in, it'll go to the 4th.

What's the best way in javascript to maintain the indexing on this without storing all found results in a variable and causing lag on the page?

Ben
  • 60,438
  • 111
  • 314
  • 488
  • 1
    How is the user changing the data? Are you making those divs "contentEditable"? Filling out a form elsewhere on the page? – Marc B Apr 26 '11 at 19:24
  • see this: http://stackoverflow.com/questions/5687332/implementing-a-fast-javascript-search/5687546#5687546 – andres descalzo Apr 26 '11 at 19:32

5 Answers5

5

I would build a data object while traversing matched elements. Then send the object so you do not have multiple ajax request from a loop. jsfiddle:

<div class='item' id='field-18583'>This is yet another test</div>
<div class='item' id='field-18585'>This is test data</div>

Script:

var searchTerm = 'This is',
    replaceWith = 'This is new',
    updateObj = {};
$("#sheet div.item:contains('" + searchTerm + "')").each(function(){
   // use id to build an update object
    updateObj[this.id.replace('field-', '')] = {
        oldText: searchTerm, 
        newText: replaceWith
    }; // not sure what you are trying to save here
   // manipulate html
   this.innerHTML = this.innerHTML.replace(searchTerm, replaceWith);
});
// after, send ajax data `updateObj`
Josiah Ruddell
  • 29,697
  • 8
  • 65
  • 67
  • @andres descalzo - if case insensitivity is desired use [this](http://stackoverflow.com/questions/187537/is-there-a-case-insensitive-jquery-contains-selector) filter from the SO community. – Josiah Ruddell Apr 26 '11 at 19:33
  • This will replace all events etc. inside the context. Rather use mark.js (https://markjs.io) or something else that keeps events working. – dude Jun 17 '21 at 10:13
1

If you must search/replace the html, then use the innerHTML property:

document.getElementById("sheet").innerHTML

But be aware that the browser keeps the DOM in memory as a tree, not as HTML. It only reads HTML to construct the DOM. So you may find element-wise changes are faster.

Phil H
  • 19,928
  • 7
  • 68
  • 105
  • The question is tagged `jquery` so the OP is probably not looking for a strictly vanilla JS solution. – Matt Ball Apr 26 '11 at 19:26
  • I understand how to get the sheet inner HTML, I'm looking more for how to search that inner HTML for the text provided, preferably only to find the "next" instance of the result. – Ben Apr 26 '11 at 19:32
1

You can select all divs with class item that are children of the element with ID sheet like so:

$('#sheet > div.item')

and you can set the text of each div.item with .text():

$('#sheet > div.item').text(function (i, oldText)
{
    return oldText.replace('something', 'somethingElse');
});

Is that what you're looking for?

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Great answer. Based on your description of the problem, you may want to use a regular expression to ensure you replace all occurrences of the string as in "replace(/something/g, 'somethingElse')". The replace method, by default, will only replace the first occurrence. – Michael Venable Apr 26 '11 at 19:30
  • You're right. The question was vague enough that it seemed best to leave my answer generic but syntactically correct. If the OP provides any more information, I'm happy to edit my answer accordingly. – Matt Ball Apr 26 '11 at 19:36
  • You can also use `String#replaceAll` to avoid RegEx overhead. –  Sep 28 '21 at 03:00
  • @MattF. how does [`replaceAll`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll) avoid regex overhead? – Matt Ball Sep 28 '21 at 16:22
  • Because `str.replaceAll('x', 'y')` does the same as `str.replace(/x/g, 'y')`, without the RegEx. –  Sep 28 '21 at 20:47
  • @MattF. I slapped together [a quick benchmark](https://jsbench.me/kakubkucbe/1) and it seems that `replace()` with a regex is somewhat faster, at least in the version of Chrome I'm using. – Matt Ball Oct 03 '21 at 18:49
0
$('#sheet').children().each(function(){
    $(this).html().replace('oldVal', 'newVal');
});
Tim Joyce
  • 4,487
  • 5
  • 34
  • 50
0

Here's a pure Javascript solution. You can store the innerHTML of #sheet to a global variable and then use the search input value in a new RegExp to replace the found text with whatever you want. So given the following HTML:

<div id='sheet'>
    <div class='item' id='field-18583'>This is yet another test</div>
    <div class='item' id='field-18585'>This is test data</div>
</div>
<input type="text" id="t" /><button id="s" onclick="searchIt()">Search</button>

You could do something like:

var sheet,
    searchIt = function() {
        var v = document.getElementById('t').value;

        sheet = (typeof sheet == "undefined") ?
                document.getElementById('sheet').innerHTML :
                sheet;

        document.getElementById('sheet').innerHTML = 
            sheet.replace(new RegExp(v, 'ig'), "<span>$&</span>");
    };

The $& in the replace represents the matched RegExp.

See working example →

mVChr
  • 49,587
  • 11
  • 107
  • 104