el Dude
el Dude

Reputation: 5183

How to replace html element with another element/text, represented in string?

I have a problem with replacing HTML elements.

For example, here is a table:

<table>
    <tr>
        <td id="idTABLE">0</td>
        <td>END</td>
    </tr>
</table>

(it can be div, span, anything)

And string in JavaScript:

var str = '<td>1</td><td>2</td>';

(It can be anything, 123 text, <span>123 element</span> 456 or <tr><td>123</td> or anything)

How can I replace element idTABLE with str?

So:

<table>
    <tr>
        <td id="idTABLE">0</td>
        <td>END</td>
    </tr>
</table>

Becomes:

<table>
    <tr>
        <td>1</td>
        <td>2</td>
        <td>END</td>
    </tr>
</table> 
<!-- str = '<td>1</td><td>2</td>'; -->

<table>
    <tr>
        123 text
        <td>END</td>
    </tr>
</table>
<!-- str = '123 text' -->

<table>
    <tr> 
        <td>123</td> 
        <td>END</td>
    </tr>
</table>
<!-- str = '<td>123</td>' -->

I tried createElement, replaceChild, cloneNode, but with no result at all.

Upvotes: 48

Views: 179942

Answers (8)

el Dude
el Dude

Reputation: 5183

The best way I found is to use the outerHTML property, but it does not have crossbrowser support yet, so I wrote my own code to manage that:

var str = '<a href="http://www.com">item to replace</a>'; //it can be anything
var Obj = document.getElementById('TargetObject'); //any element to be fully replaced
if(Obj.outerHTML) { //if outerHTML is supported
    Obj.outerHTML=str; ///it's simple replacement of whole element with contents of str var
}
else { //if outerHTML is not supported, there is a weird but crossbrowsered trick
    var tmpObj=document.createElement("div");
    tmpObj.innerHTML='<!--THIS DATA SHOULD BE REPLACED-->';
    ObjParent=Obj.parentNode; //Okey, element should be parented
    ObjParent.replaceChild(tmpObj,Obj); //here we placing our temporary data instead of our target, so we can find it then and replace it into whatever we want to replace to
    ObjParent.innerHTML=ObjParent.innerHTML.replace('<div><!--THIS DATA SHOULD BE REPLACED--></div>',str);
}

Upvotes: 74

badimo57
badimo57

Reputation: 1

I have the same situation. Here is my current code, since I couldn't do it better:

function update() {
            var game = $("a#customGame");
            games.forEach(updateGameList());
            
        }
        function updateGameList() {
            var i = 0;

            var game0 = $("span#game");
            var game1 = $("span#game1");
            var game2 = $("span#game2");
            var game3 = $("span#game3");
            var game4 = $("span#game4");
            var game5 = $("span#game5");                

            switch (i) {
                case 0:
                    game0.innerHTML = "<a href='" + gameLink + "'>" + gameName + "</a>";
                    break;
                case 1:
                    game1.innerHTML = "<a href='" + gameLink + "'>" + gameName + "</a>";
                    break;
                case 2:
                    game2.innerHTML = "<a href='" + gameLink + "'>" + gameName + "</a>";
                    break;
            }
            
            i++;
            
            if (i > 5) {
                i = 0;
            }
        }

It clearly is pretty bad. I am trying to use the createElement idea, but it failed. I am worried I am using the wrong function innerHTML. I hope this helps you guys.

Upvotes: -1

Jonathan Cross
Jonathan Cross

Reputation: 715

If you need to actually replace the td you are selecting from the DOM, then you need to first go to the parentNode, then replace the contents replace the innerHTML with a new html string representing what you want. The trick is converting the first-table-cell to a string so you can then use it in a string replace method.

I added a fiddle example: http://jsfiddle.net/vzUF4/

<table><tr><td id="first-table-cell">0</td><td>END</td></tr></table>

<script>
  var firstTableCell = document.getElementById('first-table-cell');
  var tableRow = firstTableCell.parentNode;
  // Create a separate node used to convert node into string.
  var renderingNode = document.createElement('tr');
  renderingNode.appendChild(firstTableCell.cloneNode(true));
  // Do a simple string replace on the html
  var stringVersionOfFirstTableCell = renderingNode.innerHTML;
  tableRow.innerHTML = tableRow.innerHTML.replace(stringVersionOfFirstTableCell,
    '<td>0</td><td>1</td>');
</script>

A lot of the complexity here is that you are mixing DOM methods with string methods. If DOM methods work for your application, it would be much bette to use those. You can also do this with pure DOM methods (document.createElement, removeChild, appendChild), but it takes more lines of code and your question explicitly said you wanted to use a string.

Upvotes: 2

Pebbl
Pebbl

Reputation: 35995

Because you are talking about your replacement being anything, and also replacing in the middle of an element's children, it becomes more tricky than just inserting a singular element, or directly removing and appending:

function replaceTargetWith( targetID, html ){
  /// find our target
  var i, tmp, elm, last, target = document.getElementById(targetID);
  /// create a temporary div or tr (to support tds)
  tmp = document.createElement(html.indexOf('<td')!=-1?'tr':'div'));
  /// fill that div with our html, this generates our children
  tmp.innerHTML = html;
  /// step through the temporary div's children and insertBefore our target
  i = tmp.childNodes.length;
  /// the insertBefore method was more complicated than I first thought so I 
  /// have improved it. Have to be careful when dealing with child lists as  
  /// they are counted as live lists and so will update as and when you make
  /// changes. This is why it is best to work backwards when moving children 
  /// around, and why I'm assigning the elements I'm working with to `elm` 
  /// and `last`
  last = target;
  while(i--){
    target.parentNode.insertBefore((elm = tmp.childNodes[i]), last);
    last = elm;
  }
  /// remove the target.
  target.parentNode.removeChild(target);
}

example usage:

replaceTargetWith( 'idTABLE', 'I <b>can</b> be <div>anything</div>' );

demo:

By using the .innerHTML of our temporary div this will generate the TextNodes and Elements we need to insert without any hard work. But rather than insert the temporary div itself -- this would give us mark up that we don't want -- we can just scan and insert it's children.

...either that or look to using jQuery and it's replaceWith method.

jQuery('#idTABLE').replaceWith('<blink>Why this tag??</blink>');


update 2012/11/15

As a response to EL 2002's comment above:

It not always possible. For example, when createElement('div') and set its innerHTML as <td>123</td>, this div becomes <div>123</div> (js throws away inappropriate td tag)

The above problem obviously negates my solution as well - I have updated my code above accordingly (at least for the td issue). However for certain HTML this will occur no matter what you do. All user agents interpret HTML via their own parsing rules, but nearly all of them will attempt to auto-correct bad HTML. The only way to achieve exactly what you are talking about (in some of your examples) is to take the HTML out of the DOM entirely, and manipulate it as a string. This will be the only way to achieve a markup string with the following (jQuery will not get around this issue either):

<table><tr>123 text<td>END</td></tr></table>

If you then take this string an inject it into the DOM, depending on the browser you will get the following:

123 text<table><tr><td>END</td></tr></table>

<table><tr><td>END</td></tr></table>

The only question that remains is why you would want to achieve broken HTML in the first place? :)

Upvotes: 11

laughingbovine
laughingbovine

Reputation: 460

Your input in this case is too ambiguous. Your code will have to know if it should just insert the text as-is or parse out some HTML tags (or otherwise wind up with bad HTML). This is unneeded complexity that you can avoid by adjusting the input you provide.

If the garbled input is unavoidable, then without some sophisticated parsing (preferably in a separate function), you could end up with some bad HTML (like you do in your second example... which is Bad, right?).

I'm guessing you want a function to insert columns into a 1-row table. In this case, your contents should be passed in as an array (without table, tr, td tags). Each array element will be one column.

HTML

<table id="__TABLE__"><tr><td></td></tr></table>

JS

using jQuery for brevity...

function insert_columns (columns)
{
    var $row = $('<tr></tr>');

    for (var i = 0; i < columns.length; i++)
        $row.append('<td>'+columns[i]+'</td>');

    $('#__TABLE__').empty(); // remove everything inside

    $('#__TABLE__').append($row);
}

So then...

insert_columns(['hello', 'there', 'world']);

Result

<table id="__TABLE__"><tr><td>hello</td><td>there</td><td>world</td></tr></table>

Upvotes: 1

3dgoo
3dgoo

Reputation: 15794

Using jQuery you can do this:

var str = '<td>1</td><td>2</td>';
$('#__TABLE__').replaceWith(str);

http://jsfiddle.net/hZBeW/4/

Or in pure javascript:

var str = '<td>1</td><td>2</td>';
var tdElement = document.getElementById('__TABLE__');
var trElement = tdElement.parentNode;
trElement.removeChild(tdElement);
trElement.innerHTML = str + trElement.innerHTML;

http://jsfiddle.net/hZBeW/1/

Upvotes: 11

Martin Lyne
Martin Lyne

Reputation: 3065

You would first remove the table, then add the new replacement to the table's parent object.

Look up removeChild and appendChild

http://javascript.about.com/library/bldom09.htm

https://developer.mozilla.org/en-US/docs/DOM/Node.appendChild

Edit: jQuery .append allows sting-html without removing tags: http://api.jquery.com/append/

Upvotes: 3

Hamed Tabatabaei
Hamed Tabatabaei

Reputation: 725

use the attribute "innerHTML"

somehow select the table:

var a = document.getElementById('table, div, whatever node, id')
a.innerHTML = your_text

Upvotes: -2

Related Questions