PDA

View Full Version : insertAdjacentHTML on non-dynamic table rows


sleidia
08-20-2006, 04:08 PM
Hi guys ;)

I'm encountering the following problem :

I have a server-side generated table ...



...

<tr id="row_1">
<td><a href="javascript: row_add('row_1')">add new row under row_1</a></td>
<tr>

<tr id="row_2">
<td><a href="javascript: row_add('row_2')">add new row under row_2</a></td>
<tr>

...



... and I've found a prototype script that makes insertAdjacentHTML crossbrowser ...



// PROTOTYPES

if( typeof HTMLElement != "undefined" && !HTMLElement.prototype.insertAdjacentElement ) {

HTMLElement.prototype.insertAdjacentElement = function( where , parsedNode ) {

switch ( where ) {

case 'beforeBegin':
this.parentNode.insertBefore( parsedNode ,this )
break;

case 'afterBegin':
this.insertBefore( parsedNode , this.firstChild );
break;

case 'beforeEnd':
this.appendChild( parsedNode );
break;

case 'afterEnd':

if ( this.nextSibling )
this.parentNode.insertBefore( parsedNode , this.nextSibling );
else this.parentNode.appendChild( parsedNode );
break;

}

}

HTMLElement.prototype.insertAdjacentHTML = function( where , htmlStr ) {

var r = this.ownerDocument.createRange();
r.setStartBefore( this );
var parsedHTML = r.createContextualFragment( htmlStr );
this.insertAdjacentElement( where , parsedHTML );

}

HTMLElement.prototype.insertAdjacentText = function ( where , txtStr ) {

var parsedText = document.createTextNode( txtStr );
this.insertAdjacentElement( where , parsedText );

}

}



... but, it seems that this method doesn't allow me to insert a row between two existing ones. I can only add something at the end of the document with document.body.insertAdjacentHTML .... which I don't need.

So, what row_add() should look like in order to make a row appear just below the concerned one?

Many thanks for the help !! :)

sleidia
08-20-2006, 04:29 PM
I've tried ...


document.getElementById( 'row_1' ).appendChild('<tr><td>something</td></tr>');


... but it triggers the following error :

Error: uncaught exception: [Exception... "Could not convert JavaScript argument arg 0 [nsIDOMHTMLTableRowElement.appendChild]" nsresult: "0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS)" location: "JS frame :: javascript: document.getElementById( 'row_1' ).appendChild('<tr><td>something</td></tr>'); :: <TOP_LEVEL> :: line 1" data: no]

Does it mean that it's totally impossible to append HTML to a non-dynamically produced row?

Kravvitz
08-21-2006, 06:03 AM
appendChild() takes a node reference as it's argument. A string is not a node reference.

I'm assuming row_1 is a suitable parent element for a <tr>. Try this:
var tr = document.createElement('tr');
var td = document.createElement('td');
td.appendChild(document.createTextNode('something'));
tr.appendChild(td);
document.getElementById('row_1').appendChild(tr);

sleidia
08-21-2006, 08:52 AM
Thanks Kravvitz :)

But how do I do when I want to apply an id to a newly created row?
For example, the id between row_1 and row_2 should be row_3.

Every newly created row should have it's own id so that I can also add a new rows just below it. This would give the freedom to insert row anywhere in the table.

Thanks for the help again !

Kravvitz
08-21-2006, 09:05 AM
You're welcome :)

You can do it like this
var rowNum = 3;
var tr = document.createElement('tr');
tr.setAttribute('id','row'+rowNum);
...
OR the way I prefer
var rowNum = 3;
var tr = document.createElement('tr');
tr.id = 'row'+rowNum;
...
Keep in mind that at least one browser needs elements to be added into the DOM before some attributes can be set. Unfortunately, I don't remember the specifics, so you'll just have to test your code thoroughly.

sleidia
08-21-2006, 09:55 AM
That's great Kravvitz ! :)

Thanks to you, I've managed to create this function :


function table_row_add ( row_parent , cell_content ) {

if ( !row_num ) var row_num = document.getElementById( 'table' ).getElementsByTagName( 'tr' ).length;

var tr = document.createElement( 'tr' );
var td = document.createElement( 'td' );

row_num++;

td.appendChild( document.createTextNode( cell_content ) );
tr.setAttribute( 'id' , 'row_' + row_num );
tr.appendChild( td );

document.getElementById( row_parent ).appendChild( tr );

}



The problem is that table_row_add( 'row_1' , 'something' ) displays the string "something" on the right side and not under row_1 !!! And since, it's dynamically created, viewing the source is helpless :( [edit : it works normally on IE. The problem is on FF and Opera ]

Also, for row_num, instead of grabing the total number of TRs, I'd like to grab only the ones that have an id. But getElementsByTagName( 'id' ).length outputs zero because .... it's not a tag! ... ahahah ... :)

Kor
08-21-2006, 10:37 AM
Does your table has rows without id's as well? You have not mentioned this... On the other hand, why do you need an id? Any row is to be found easily upon it's index in the rows collection.

How in fact looks like your table?

An essential not as well: Rows are to be appended to the TBODY, not to the TABLE. TBODY might be facultative in HTML, but it is compulsory in javascript.

Kravvitz
08-21-2006, 10:46 AM
sleidia, use Firefox's DOM Inspector. If it's installed it's in the Tools menu. If it's not installed, you'll need to install Firefox again over your current installation and select the Developer Tools option when you do.

Kor makes good points. I should have mentioned the <tbody> thing. Thanks for bringing it up, Kor. :)

sleidia
08-21-2006, 10:48 AM
Hello Kor :)

Does your table has rows without id's as well? You have not mentioned this... On the other hand, why do you need an id? Any row is to be found easily upon it's index in the rows collection.

You're right, there are rows without ids too.
And you are right too by saying I don't absolutely need to use ids ;)

An essential not as well: Rows are to be appended to the TBODY, not to the TABLE. TBODY might be facultative in HTML, but it is compulsory in javascript.

But, if I append the new row to TBODY, the new row will only appear at the bottom of the table, right? I would prefer to append it to TR, actually .... if it is possible, of course ;)

How in fact looks like your table?

this :



<table>

<form>

<tr>
<td>title</td>
<tr>

<tr id="row_1">
<td><a href="javascript: row_add('row_1')">add new row under row_1</a></td>
<tr>

<tr id="row_2">
<td><a href="javascript: row_add('row_2')">add new row under row_2</a></td>
<tr>

<tr>
<td>submit button</td>
<tr>

</form>

</table>

Kravvitz
08-21-2006, 10:54 AM
By using insertBefore() instead of appendChild(), you can place it elsewhere in the <table>.

Be sure to read my previous post. You may have been writing your response to Kor's post when I wrote mine.

sleidia
08-21-2006, 10:59 AM
sleidia, use Firefox's DOM Inspector. If it's installed it's in the Tools menu. If it's not installed, you'll need to install Firefox again over your current installation and select the Developer Tools option when you do.

Kor makes good points. I should have mentioned the <tbody> thing. Thanks for bringing it up, Kor. :)

Thanks Kravvitz !

I had a look with DOM inspector, and I found out that the new row is wrongly inserted like this :



...

<tr id="row_1">
<td><a href="javascript: row_add('row_1')">add new row under row_1</a></td>
<tr id="row_11"><td>something</td></tr>
<tr>

<tr id="row_2">
<td><a href="javascript: row_add('row_2')">add new row under row_2</a></td>
<tr>

...

Kor
08-21-2006, 11:01 AM
You can not append a row to another row... Anywhere you want to insert the new rows, they are to be inserted as the TBODY's childs.

If you want to append a new row after of before an element, you may use a workaround with insertBefore() method and nextSibling reference

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<script type="text/javascript">
function addRow(){
var root=document.getElementById('row1').parentNode;//this is the TBODY
var nextS=document.getElementById('row1').nextSibling;//this is the next sibling
var row=document.createElement('tr');
var cell=document.createElement('td');
cell.appendChild(document.createTextNode('inserted row'))
row.appendChild(cell);
root.insertBefore(row,nextS)
}
</script>
</head>
<body>
<table width="300px" border="1" cellspacing="0" cellpadding="0">
<tr>
<td>&nbsp;</td>
</tr>
<tr id="row1">
<td>1</td>
</tr>
<tr>
<td>&nbsp;</td>
</tr>
</table>
<br>
<span onclick="addRow()">click here to add the row</span>
</body>
</html>

sleidia
08-21-2006, 11:25 AM
WOW !!!!!!!!! :) .... it finally worked !!! :)

That was a great help from both of you, guys.

=> http://www.codingforums.com/showthread.php?p=478199#post478199 :)

And, the final piece of ...


function table_row_add( row_parent , cell_content ) {

if ( !row_num ) var row_num = document.getElementById( 'table' ).getElementsByTagName( 'tr' ).length;

var tbody = document.getElementById( row_parent ).parentNode;
var next_elem = document.getElementById( row_parent ).nextSibling;

var tr = document.createElement( 'tr' );
var td = document.createElement( 'td' );

row_num++;

td.appendChild( document.createTextNode( cell_content ) );
tr.setAttribute( 'id' , 'row_' + row_num );
tr.appendChild( td );

tbody.insertBefore( tr , next_elem );

}




Oh .... I almost forgot : VIVA ROMANIA ! .... ahahahaha ;)

sleidia
08-22-2006, 09:07 AM
Hi guys :) ..... may I ask one more question? :o

In the code above, what should I change if I wanted to instert new rows above row_parent instead of below?

I've tried two different tactics but none worked as expected.

One was to use prevSibling but I didn't know how to use it correctly.
The second one was to simply substract one to row_parent but it causes misplacement of rows under certain conditions.

Again, thanks for the help !

Kravvitz
08-22-2006, 09:46 AM
Simply change
var next_elem = document.getElementById( row_parent ).nextSibling;
to
var next_elem = document.getElementById( row_parent );

sleidia
08-22-2006, 10:31 AM
Wow .. I feel so dumb now :(
I hope I'll be able to finally grasp the logic behind the DOM elements !

Many many thanks, Kravvitz !

Kravvitz
08-22-2006, 10:57 AM
You're welcome :)

Don't feel to bad. That happens to most of us once in a while.

sleidia
08-22-2006, 01:09 PM
Gosh ..... I'm driving totally crazy :(

I've added a table_row_del() function that should work normally since the alert displays the right element id and since the dynamic ids are placed correctly as seen in the DOM inspector sample.

But instead, only the first row of the table gets deleted!
So, everytime I run table_row_del(), the table shrinks form its top.

I simply don't get it :(



function table_row_del( row_this ) {

document.getElementById( 'table' ).deleteRow( row_this );
alert( row_this );

}




<table cellspacing="0" cellpadding="0" border="0" id="table">

<form>

<tbody>

<tr id="row_0"></tr>
<tr id="row_1"></tr>
<tr id="row_2"></tr>
<tr id="row_3"></tr>
<tr id="row_4"></tr>
<tr id="row_5"></tr>
<tr id="row_6"></tr>
<tr id="row_7"></tr>

<tr id="row_13">
<td id="cell_13" style="padding: 0px;">
<input type="text" maxlength="55" class="field1" name="form_ecom_cat_name[]"/>
<a class="warnlinknorm" href="javascript:table_row_del( 'row_13' );"> [ - ] </a>
</td>
</tr>

<tr id="row_8">
<td style="padding-left: 0px;">
<input type="text" maxlength="55" class="field1" value="cat_4" name="form_ecom_cat_name[]"/>
<a class="warnlinknorm" href="javascript: table_row_add( 'before' , 'row_8' , 0 , cats_html_temp ); defocus(this); "> [ + ↑ ] </a>
<a class="warnlinknorm" href="javascript: table_row_add( 'after' , 'row_8' , 0 , cats_html_temp ); defocus(this); "> [ + ↓ ] </a>
<a class="warnlinknorm" href="javascript: table_row_add( 'after', 'row_8' , 30 , cats_html_temp ); defocus(this); "> [ + → ] </a>
</td>
</tr>

</tbody>

</table>

Kravvitz
08-22-2006, 08:44 PM
deleteRow() takes an index number. You're passing it a string.

Try this:
var row = document.getElementById( row_this );
row.parentNode.removeChild( row );

Kor
08-22-2006, 08:48 PM
You have a series of TR without any TD, which is a HTML error
And better use removeChild() method

Once again.. try to think dynamically. Don't give ids. Use the index of the elements.

Kor
08-22-2006, 09:01 PM
and... a matter of HTML validation.

the sequence:
<table>
<form>
....
</form>
</table>

will remove that stupid extra space on the bottom of the table, I know. It happends merely in IE, as IE thinks that FORM is a block-level element.

But it is an invalid HTML code, as you must not insert extra tags between table's elements. To get rid of that space you may simply give the form a display inline

<form style="display:inline">
<table>
....
</table>
</form>

Or even better use an embeded or external CSS with:

form {
display:inline;
}

Kravvitz
08-22-2006, 09:34 PM
But <form> is a block-level element. It just is silly for it to have margins by default. I usually give it margin:0; instead of display:inline;.

sleidia
08-22-2006, 11:13 PM
deleteRow() takes an index number. You're passing it a string.

Try this:
var row = document.getElementById( row_this );
row.parentNode.removeChild( row );

for ( i=1; i>0; i++ ) {

alert('THANKS !!!');

}

:D

sleidia
08-22-2006, 11:24 PM
Hi Kor :)

You have a series of TR without any TD, which is a HTML error

Those are simply non-expanded nodes of the DOM inspector.
The HTML is OK.


Once again.. try to think dynamically. Don't give ids. Use the index of the elements.

Sorry, but I fail to see what the benefits would be.
Using the nodes array doesn't sound wise when you have TRs that you want to ignore for example. And the div names can be created dynamically with setAttribute, right? Lastly, I find the divs easier to track because you see them in the HTML code. Counting the TR tags? No thanks ;)

Kor
08-23-2006, 10:33 AM
Hi Kor :)
Those are simply non-expanded nodes of the DOM inspector.
The HTML is OK.

No it is not. Try to validate (http://validator.w3.org/) a sequence

<table>
<form>
<tr>
<td>
</td>
</tr>
</form>
</table>


And you will see...

But <form> is a block-level element. It just is silly for it to have margins by default. I usually give it margin:0; instead of display:inline;.

Yes, margin:0 is as good as a display:inline. On the other hand, as long as the form's elements are iniline-leveled elements, I see no reason for a form to be a block one. Anyway, the default display level of the form is not important. After all the important thing remains that a form should nest the whole table, and must have margin 0 to avoid spaceing.

Kravvitz
08-23-2006, 10:52 AM
The Strict variants of HTML 4.01 and XHTML 1.0 only allow direct children of <form> to be block-level elements, <del>, <ins>, or <script>. Hence I use margin:0;.

I agree the that important thing is that the elements are properly nested.

Kor
08-23-2006, 11:37 AM
The Strict variants of HTML 4.01 and XHTML 1.0 only allow direct children of <form> to be block-level elements, <del>, <ins>, or <script>.
Aha... Thanks, that is good to know.