Change id of a Table row using javascript in IE - javascript

Hello I have a table which has a few rows. And I have an add button in my application which allows to add rows at any rowIndex. I use insertRow method to insert rows at a specific position. So once a row in inserted, I change the table row Ids of all the rows, to arrange them in an ascending order. Now I have been developing this application in FF and it has been coming pretty well. Now I am making a few changes to the code to make it work in IE. But this just does not work in IE. I have been testing this code for the last two days, it works in FF and chrome, but not in IE. I am not sure, what is the mistake I am making. I am just recreating the situation with an example, and this is the code for that example. Please help me out and tell me what could be the mistake I am making for it not to be working in IE. Any suggestions would be a great help.
<html>
<head>
<script type = 'text/javascript'>
function getIds()
{
var elem = document.getElementsByTagName("tr");
for(var i in elem)
{
if(elem[i] && elem[i].id!=null && elem[i].id!='')
alert(elem[i].id);
}
}
function changeIds()
{
var elem = document.getElementsByTagName("tr");
for(var i in elem)
{
if(elem[i] && elem[i].id!=null && elem[i].id!='')
{ index = Number(elem[i].rowIndex)+1; elem[i].id = "tabsf_"+index;}
}
alert('change');
}
</script>
</head>
<body>
<table id="tabsf">
<tbody>
<tr id="tabsf_1"><td>1</td><td>2</td></tr>
<tr id="tabsf_2"><td>3</td><td>4</td></tr>
<tr id="tabsf_5"><td>5</td><td>6</td></tr>
<tr id="tabsf_3"><td>7</td><td>8</td></tr>
<tr id="tabsf_4"><td>9</td><td>10</td></tr>
</tbody>
</table>
<table><tr><td><input type="button" name="hach" value="getIds" onclick="getIds()" /></td>
<td><input type="button" name="hach" value="Change Ids" onclick="changeIds()" /></td></tr></table>
</body>
</html>
</code>

The problem is that for..in loop doesn't ensure order. It is one of the gotchas why you should avoid them for arrays. (there is another, see my link)
Use simple for loops instead ( for i..n )
function getIds() {
var elem = document.getElementsByTagName("tr");
for (var i = 0; i < elem.length; i++) {
if (elem[i].id) {
alert(elem[i].id);
}
}
}
function changeIds() {
var elem = document.getElementsByTagName("tr");
for (var i = 0; i < elem.length; i++) {
if (elem[i].id) {
index = Number(elem[i].rowIndex) + 1;
elem[i].id = "tabsf_" + index;
}
}
alert('change');
}
Works perfectly cross-browser.

I would say try using the setAttribute function vs the id property to change the id.

Update: Try #Spinon's idea first.
Update: It seems to in fact have been an ID collision, something IE rightly prevented from happening and the other browsers happily let through.
Original answer:
My guess is that your loop creates an id collision: An id is set twice while the loop runs. IE could (rightly) be blocking the setting of a duplicate ID.
I would try unsetting the IDs first:
for(var i in elem)
{
if(elem[i] && elem[i].id!=null && elem[i].id!='')
elem[i].id = elem[i].id + "_temp";
}
for(var i in elem)
{
if(elem[i] && elem[i].id!=null && elem[i].id!='')
{ index = Number(elem[i].rowIndex)+1;
elem[i].getElementsByTagName("td")[0].innerHTML = "tabsf_"+index;
elem[i].id = "tabsf_"+index;}
}
However, I don't understand how you use row IDs to change the sorting?

Related

On a target page with multiple id's, script is acting only on the first one

I'm filling in a text box using Greasemonkey. It's working, but the page has more than one <input> with the same id, and GM is only filling in the first one.
The page's HTML (repeated 3 times):
<input type="text" id="se" value="" class="form">
My GM Code:
document.getElementById("se").value = "na";
How can I set the 2nd or 3rd <input> too?
Yeah, pages with malformed HTML are a right pain. Fortunately, querySelectorAll() usually works as one would hope on such pages. (Alas, libraries, even jQuery, usually don't handle the malformed pages as well.)
In this case, the following code should work for you:
var badInputs = document.querySelectorAll ("#se");
for (var J = badInputs.length - 1; J >= 0; --J) {
var tInput = badInputs[J];
tInput.value = "na";
}
You can see the code in action at jsFiddle.
You could iterate the input elements on the page, looking for a specific id value:
var elements = document.getElementsByTagName('input');
for(var i = 0; i < elements.length; i++)
{
if(elements[i].id === "se")
{
elements[i].value = "na";
}
}
fiddle

Finding a row with a cell with a specific value

I have to mark all rows in a table (and later change the code to delete the rows).
The table structure looks like:
<table class="table table-striped table-condensed" id="buckettable">
<thead>
...cut out
</thead>
<tbody>
<tr class="success">
<td><input class="fld-bucketno" type="checkbox" name="bucket" value="158bf61f-8f66-4dee-9ff6-b9d1f6d4b840" /></td>
<td class="text-right">
<a class="bucketNo" href="/Sim/Staging/1212">1212</a>
</td>
What I am interested in is the value of the anchor in the td - second last line.
I get a json list of id's to delete. First I mark them, then change the code to delete them. That is the idea, at least. Mind me - I know a lot of programming, but Javascript is an unknown land for me ;)
My code so far is:
success: function (resp) {
var table = $('#buckettable').find('tbody')[0];
var length = resp.length;
for (var i = 0; i < length; i++) {
var bucketNo = resp[i];
var rows = table.children.length;
for (var j = 0; j < rows; j++) {
var row = table.children[j];
var bucket = row.find('.bucketNo').text();
if (bucket == bucketNo) {
alert(bucket);
}
}
}
$('#cleanup').modal('hide');
and I fail to filter the rows. I am open to any sensible other approach - hopefully it teaches me a lot.
In the above code I manage to get the row... but then the find('.bucketNo') fails with an aexception that Object # has no method find.... which I find funny because I use that on a form earlier. Pointers to documentation VERY welcome, especially in addition to an answer.
If there is a better way to do that tell me. I was told that search by ID is faster (obviously) but somehow I am not sure - should I set a coded ID (bucket-xxx, xxx being the number) on every row?
There is a much simpler way of doing this.
var targetBucketNo = 1212;
$('#buckettable a.bucketNo')
.filter(function(item) {
return item.text() == targetBuvketNo;
})
.parent()
.remove();
To explain in more detail. The following will get the anchors with the bucketNo class that are inside your #buckettable table.
$('#buckettable a.bucketNo')
Filter will filter the results for ones that have the target bucket number
.filter(function(item) {
return item.text() == targetBuvketNo;
})
Remove will remove the elements
.remove();
You can replace .remove() with .addClass('your-class-name') if you wanted to add a class instead. This would add it to the td element so you should add .parent() before addClass.
You are accessing the native Javascript children, hence find() is not a function. However you can skip around a lot of the native functions you're using by using the jQuery filter method:
var $aTag = $('#buckettable').find('tbody td a.bucketNo').filter(function() {
return $(this).text() == bucketNo
});
alert($aTag.text());
You can also loop all links with the class "bucketNo" and then look if the ID is in your array. After this get the containing TR and delete it or add a class or something:
var resp = [2323,5656]
$('a.bucketNo').each(function() {
if( resp.indexOf( parseInt($(this).text()) ) != -1 )
$(this).closest('tr').addClass('del');
});
http://jsfiddle.net/44cEt/

par.rowIndex is null in this code

I'm really new to JavaScript but I can't find out why this program won't work.
I want when I click the dynamically created button which is situated in a cell in my dynamically created table to get the rowindex of the row in which the button is situated.
Thanks in advance - here is my code:
<html>
<head>
<script>
function whichrow(obj) {
var par = obj.parentNode;
while(par.nodeName.toLowerCase()! = 'tr') {
par = par.parentNode;
}
alert(par.rowIndex);
}
</script>
</head>
<body>
<script>
mybody = document.getElementsByTagName("body")[0];
mytable = document.createElement("table");
mytablebody = document.createElement("tbody");
for(var i = 0; i<5; i++) {
mycurrent_row = document.createElement("tr");
mycurrent_row.id = "row"+i;
for(var j = 0; j<4; j++) {
mycurrentcell = document.createElement("td");
currenttext = document.createTextNode("Row" + i + "Column" + j);
mycurrentcell.appendChild(currenttext);
mycurrent_row.appendChild(mycurrentcell);
}
mybutoncell = document.createElement("td");
but = document.createElement("button");
mybutoncell.appendChild(but);
mycurrent_row.appendChild(mybutoncell);
mytablebody.appendChild(mycurrent_row);
but.onClick = whichrow(this);
}
mytable.appendChild(mytablebody);
mybody.appendChild(mytable);
mytable.setAttribute("border", "2");
</script>
</body>
</html>
Ok, so a few points to note here.
In the javascript eventing system, the system calls your callback with its own event object containing different properties according to what happened.
So, here are the mistakes:
When you're assigning to the event handler, when you say but.onclick = whichrow(this) you're setting but.onclick to the result of whichrow, which is undefined since you're not returning anything anyway. It should be but.onclick = whichrow; which will call whichrow when the user clicks your button. The parameter passed is a MouseEvent object. The link I've supplied should serve as a good start to read up on what kind of properties are available to you.
I have to check, since I use el.addEventListeners a lot, but onclick needs to be in lower case, not camelCase like you've done.
Inside the event callback, this usually refers to the element that was clicked, so you should use that.
There is no rowIndex property.
Now, trying to find a solution to your problem. rowIndex can be gleaned by traversing the dom. I'm not sure what purpose this will serve since you're creating the DOM by hand anyway and know the rowIndex already, but if it were unknown, here's what I would do
function whichRow(e) {
// here this should be the button.
var curRow = this.parentElement.parentElement,
rowIndex = 0;
while(curRow) {
curRow = curRow.previousElementChild;
rowIndex++;
}
return rowIndex;
}
I'm writing this off the top of my head, but the point is to give the main idea. In the above snippet, I've taken the parent of the parent of the button, since here's the approximate markup of the button section:
<tr>
<td>
<button></button>
</td>
</tr>
so, the parentElement of the parentElement of the button element should give you the <tr>. Then we'll traverse backwards till we don't have any previous elements, counting as we go. Once the previous element is null, return the count.
The obj you are passing to whichrow() is a button, which I assume inside a TD. So your while loop will exit in its first iteration itself, resulting in par holding a TD - which does not have a property named rowIndex

Script to enable/disable input elements?

I'm wondering if it's possible for a script to enable/disable all input elements on the page with some sort of toggle button.
I googled it but didn't find anything too useful except for this:
http://www.codetoad.com/javascript/enable_disable_form_element.asp
but I'm not sure how to edit it for the toggle.
Something like this would work:
var inputs=document.getElementsByTagName('input');
for(i=0;i<inputs.length;i++){
inputs[i].disabled=true;
}
A working example:
$().ready(function() {
$('#clicker').click(function() {
$('input').each(function() {
if ($(this).attr('disabled')) {
$(this).removeAttr('disabled');
}
else {
$(this).attr({
'disabled': 'disabled'
});
}
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<input type='text'></input>
<input type='text'></input>
<input type='text'></input>
<div id='clicker' style='background-color:#FF0000; height:40px; width:100px;'></div>
Here is a function to toggle all inputs on the page:
function toggle_inputs() {
var inputs = document.getElementsByTagName('input');
for (var i = inputs.length, n = 0; n < i; n++) {
inputs[n].disabled = !inputs[n].disabled;
}
}
It works by using the logical NOT operator (the exclamation point), which returns the opposite of the operand. For example, !true will return false. So by using !inputs[n].disabled, it will return the opposite of what it's currently set to, thereby toggling it.
If you need code to bind the click event to the button:
document.getElementById('your_button_id').onclick = toggle_inputs;
You can also use addEventListener, but see the linked page for more information, including compatibility with Internet Explorer. The code I gave above should work across all browsers with no trouble.
for (var i = 0; i < document.getElementyByTagName('input').length; i++) {
document.getElementsByTagName('input')[i].disabled = 'disabled';
}
http://code.google.com/p/getelementsbyclassname/
^^Robert Nyman has a "get elements by class" script. Basically you'd just assign all those input elements to the same class, and then do something like:
//Collapse all the nodes
function collapseNodesByClass(theClass){
var nodes = getElementsByClassName(theClass);
for(i = 0; i < nodes.length; i++){
nodes[i].style.display='none';
}
}
This is a piece of code I'm actually currently using to collapse everything with a given class name (it uses the script I mentioned above). But in any case I think the key to your problem is being able to refer to multiple elements at once, which that script will help you with.
Also the link in your question didn't work for me :(.

How to modify HTML table with JavaScript to increase rowspan, without causing reflow?

To change HTML table so that some cell has given rowspan="n" (to increase rowspan) you have to delete n cells below the one that is getting extended.
The original HTML source (HTML structure) looks like this
<table border="1">
<tr id="1"><td>1</td><td>Zubenelgenubi</td></tr>
<tr id="2"><td>2</td><td>Algorab</td></tr>
<tr id="3"><td>3</td><td>Almach</td></tr>
<tr id="4"><td>4</td><td>Alula_Borealis</td></tr>
<tr id="5"><td>5</td><td>Rigil_Kentaurus</td></tr>
<tr id="6"><td>6</td><td>Menkent</td></tr>
</table>
and I like to transform it to something like this:
<table border="1">
<tr id="1"><td>1</td><td>Zubenelgenubi</td></tr>
<tr id="2"><td>2</td><td>Algorab</td></tr>
<tr id="3" rowspan="3"><td>3</td><td>Almach</td></tr>
<tr id="4"> <td>Alula_Borealis</td></tr>
<tr id="5"> <td>Rigil_Kentaurus</td></tr>
<tr id="6"><td>6</td><td>Menkent</td></tr>
</table>
Unfortunately SO formatting doesn't support tables, neither in Markdown, nor in HTML.
Is it possible to do it without causing unnecessary reflow? I mean here something better than simply
for (var i = 0; i < numlines; i++) {
...
if (i === 0) {
td.rowSpan = numlines;
...
} else {
tr.deleteCell(0); // or td.parentNode.removeChild(td);
}
}
which I think causes reflow after each iteration.
When adding elements one can use DocumentFragment; what to do when modifying number of elements at once?
Edit: added 03-05-2011
A solution using Range object (the W3C DOM version)
var range = document.createRange();
range.setStartBefore(document.getElementById(start+''));
range.setEndBefore(document.getElementById(start+numlines+''));
var fragment = range.cloneContents();
for (var i = 0; i < numlines; i++) {
var rownum = start + i;
var row = fragment.getElementById(rownum.toString()); // not always work
var td = row.firstChild;
if (i === 0) {
td.style.backgroundColor = 'yellow';
td.rowSpan = num.toString();
} else {
td.parentNode.removeChild(td);
}
}
range.deleteContents();
var rowAfter = document.getElementById(start+num+'');
rowAfter.parentNode.insertBefore(fragment, rowAfter);
Note that for some reason fragment.getElementById didn't work for me, so I had to cheat knowing what nodes are there.
deleteContents + insertBefore is needed because table.replaceChild(range, fragment); does not work, unfortunately (where table is element from which range was extracted).
Try to make the table element display:none
before the loop and restore the
display after.
Another option would be to assign
fixed dimensions and use
overflow:hidden for the the time of loop body.
This should isolate update tree by
the table only. Theoretically.
And the last is to compose HTML of
the table and replace the table as
whole - this will be made in single
transaction.

Categories

Resources