I am trying to use java-script to export html data into excel. The funny thing is that it DOES work when I use getElementsByTagName instead of getElementById. However, I need to pinpoint id elements and thus 'getElementById' is what I need (I guess). When I debug the below code in IE it gives me:
Object doesn't support property or method 'getElementsById'
Here is what I've got:
HTML (as an idea only):
<body>
<table>
<tr>
<td>content 1</td>
<td>content 2</td>
<td id="R">content I need</td>
<td>some other content</td>
</tr>
</table>
</body>
and accompanying JS
<script type="text/javascript">
function write_to_excel()
{
str="";
var mytable = document.getElementById("R")[0];
var row_Count = mytable.rows.length;
var col_Count = mytable.getElementById("R")[0].getElementById("R").length;
var ExcelApp = new ActiveXObject("Excel.Application");
var ExcelSheet = new ActiveXObject("Excel.Sheet");
ExcelSheet.Application.Visible = true;
for(var i=0; i < row_Count ; i++)
{
for(var j=0; j < col_Count; j++)
{
str= mytable.getElementById("R")[i].getElementById("R")[j].innerHTML;
ExcelSheet.ActiveSheet.Cells(i+1,j+1).Value = str;
}
}
}
</script>
I have the feeling - it's trifle but ... Thanks in advance!)
The getElementById method returns a single DOM element (if you have more than one HTML element with the same ID then your page is buggy but browsers won't complain because 10 years ago it was a common bug that lots of people make). As such the statement:
document.getElementById("R")[0]
Makes no sense whatsoever. Instead, what you want is:
var myTD = document.getElementById("R");
If you have a page structure like this:
<table id='T'>
<tr>
<td>content 1</td>
<td>content 2</td>
<td>content I need</td>
<td>some other content</td>
</tr>
</table>
And want to iterate each column in each row, you'd do it like this:
var mytable = document.getElementById("T");
var table_rows = mytable.getElementsByTagName('tr');
for (var row=0;row<table_rows.length;row++) {
var row_columns = table_rows[row].getElementsByTagName('td');
for (var col=0;col<row_columns.length;col++) {
var item = row_columns[col];
// process item here
}
}
See the documentation of HTMLElement for more info on how to navigate the DOM: https://developer.mozilla.org/en/docs/Web/API/Element
Full documentation of the DOM API: https://developer.mozilla.org/en/docs/DOM
You may also check out the relevant docs on MSDN instead of MDN for IE specific stuff but I prefer MDN because it documents the compatibility level (what other browsers implement a feature) of the API.
IDs must be unique.
Therefore, the function you're looking for is called getElementById (singular)
Related
I'm trying to create an if function that'd check if the second tr's class is success or danger. Let's say that it'd set the value of x to 0 if it's success and it'd set the value of z to 0 if it's danger. I'm not qute sure if it's even possible to do with Javascript but any help is appreciated. Here's some code that I thought can be useful to answering this question, but I can't get my head around it.
function hasClass(element, className) {
return element.className && new RegExp("(^|\\s)" + className + "(\\s|$)").test(element.className);
}
var myDiv = document.getElementById('myBetsTable');
hasClass(myDiv, 'success');
P.S. These classes are constantly changing.
P.S.2 It must be coded in plain Javascript
The bootstrap success and danger classes are used as a method of color coding. This leads me to believe this is some sort of sports betting website.
Because of the sensitivity of this information, it would be much better to enclose this on the server side. This code should definitely not be used in production by itself, but is mainly used to provide a minimal example.
JavaScript Solution
You'll want to create a collection of tr elements by calling getElementsByTagName
In this example, I will be using the onclick event attribute to listen for a mouse click. In a production application, this would be used some other way. Once the event has been recorded, the function to check the last bet (win or loss) will be recorded.
To get the last table row, we can use rows[rows.length-1]. We need to use this, because JS arrays are zero indexed.
I've provided a new function for adding new rows to the table. In sports betting, the tables are not fixed, but can change dynamically. Once again, in production this would look much differently.
Upon clicking Grade Ticket, a new row will appear. As sports is random and betting is discouraged, you are not guaranteed to win. So, you will either randomly win or randomly lose an event. You can then click Set Variables again to see the new result. It will change.
var rows = document.getElementsByTagName("tr");
var betsTable = document.getElementById("bets-table");
function setVariables() {
var lastBet = rows[rows.length - 1];
alert(lastBet.classList.contains("success") ? "x = 0" : "z = 0");
}
function gradeTicket() {
var gradedWager = document.createElement("tr");
var matchup = document.createElement("td");
var result = document.createElement("td");
if (Math.random() > 0.5) {
gradedWager.classList.add("success");
result.innerHTML = "WIN";
}
else {
gradedWager.classList.add("danger");
result.innerHTML = "LOSS";
}
var teams = document.createTextNode("Team A versus Team B");
matchup.appendChild(teams);
gradedWager.appendChild(matchup);
gradedWager.appendChild(result);
betsTable.appendChild(gradedWager);
}
.success { background: #5cb85c; }
.danger { background: #d9534f; }
#bets-table td {
padding: 10px;
}
#check-last-bet {
margin-top: 5%;
}
<table id="bets-table">
<tbody>
<tr class="success">
<td>Pacers versus Lakers</td>
<td>WIN $1400</td>
</tr>
<tr class="danger">
<td>Celtics versus Clippers</td>
<td>LOSS $1250</td>
</tr>
<tr class="success">
<td>Bulls versus Warriors</td>
<td>WIN $300</td>
</tr>
<tr class="danger">
<td>Oregon versus Ohio State</td>
<td>LOSS $450</td>
</tr>
<tr class="success">
<td>Manchester United versus Chelsea</td>
<td>WIN $500</td>
</tr>
</tbody>
</table>
<button id="check-last-bet" onclick="setVariables()">Check Last Bet</button>
</br></br>
<button id="grade-ticket" onclick="gradeTicket()">Grade Ticket</button>
In modern browsers faster and easier way to do this would be use the native document.querySelector method along with the nth-child CSS selector.
This avoids scraping through all the TR elements on the page.
var tr = document.querySelector("#myBetsTable tr:nth-child(2)");
var cls = tr.getAttribute('class');
if( cls === 'danger' ) {
console.log(cls);
} else {
console.log('not danger')
}
See jsfiddle example
You can create a function that checks the class using the classList property on DOM objects.
function checkClass() {
var td = document.querySelectorAll('tr')[1];
if (td.classList.contains('success')) {
x = 0;
}
else if (td.classList.contains('danger')) {
z = 0;
}
If the classes are dynamically changing you can tie the function to an event listener or AJAX call or however the classes change.
You can get the class by counting the rows and get the classname of last row
var x = document.getElementById("myTable").rows.length;
alert(document.getElementById("myTable").rows[x-1].className);
function myFunction() {
var x = document.getElementById("myTable").rows.length;
alert(document.getElementById("myTable").rows[x-1].className);
}
<table id="myTable">
<tr class="0">
<td>Row1 cell1</td>
<td>Row1 cell2</td>
</tr>
<tr class="1">
<td>Row2 cell1</td>
<td>Row2 cell2</td>
</tr>
<tr class="2">
<td>Row3 cell1</td>
<td>Row3 cell2</td>
</tr>
</table>
<button onclick="myFunction()">Get Class</button>
How can I get access to the old Node and replace it?
My XHTML has this structure:
<table id="output">
<tr>
<td>Sometext</td>
<td>...</td>
<td>...<td>
</tr>
<tr class="CLASSNAME">
<td class="ElemKat">text</td>
<td class="ELEM">...</td>
<td class="EL">...</td>
</tr>
<tr class="CLASSNAME">
<td class="ElemKat">text2</td>
<td class="ELEM">s</td>
<td class="EL">sss</td>
</tr>
</table>
I have so many classes, because the elements are create dynamically and so the IDs are not a variant.
And the function, in which the error shows up (error as a comment):
var sort=function(cmpFunc) {
var table=document.getElementById("output");
var kat=document.getElementsByClassName("ElemKat");
var elem_kat=new Array();
for(var i=0; i<kat.length; i++) {
elem_kat.push(kat[i]);
}
var elem_kat_old=new Array();
elem_kat_old=elem_kat;
elem_kat.sort(cmpFunc);
for(var j=0; j<elem_kat.length; j++) {
//table=elem_kat_old[j].parentNode; ->No error,but no change in the output
table.replaceChild(elem_kat[j],elem_kat_old[j]); //Node was not found
}
}
Instead of elem_kat_old = elem_kat;, try this:
elem_kat_old = elem_kat.slice(0)
Assigning an array to another array makes it so that whatever you do to one array, happens to the other array. This isn't quite assignment by reference, but I am not 100% sure of the proper Javascript term for it.
I have a hta app that parses some xml using xslt and populates a div within the hta page with the result. The result contains a table that has a summary of the xml with hyperlinks on the counts of xml elements - example below
There will be a button on the page, outside the "content" div that will allow the user to save the page to a static html file without the hyperlinks. I have the following javascript below, but when clicking the "Save" button the hyperlink is only removed from the first item, not the second..(there will be many more actual hyperlinks in the proper version). What am I doing wrong - I assume it is something to do with the loop....and must be done in javascript, no jquery etc - long story as to why...
function SaveContent() {
var myContent = document.getElementById("content")
var myLinks = myContent.getElementsByTagName('a')
for (var myItem = 0; myItem < myLinks.length; myItem++) {
var myChild = myLinks[myItem]
var myParent = myChild.parentNode
var myValue = myChild.innerText
myChild.parentNode.removeChild(myChild)
myParent.innerText = myValue
/*code to save to file will go here */
}
}
<div id="content">
<table>
<tr>
<td>Status</td>
<td>Count</td>
</tr>
<tr>
<td>New</td>
<td>
34
</td>
</tr>
<tr>
<td>Closed</td>
<td>
78
</td>
</tr>
</table>
</div>
Many thanks
getElementsByTagName is a live list. When you call removeChild on an item, it is removed from the list... but the index of your loop continues on!
A typical solution is:
for( var myItem = myLinks.length-1; myItem >= 0; myItem--)
ie. working from the end to the beginning. This is also good if your code might add more links, as they will not be iterated.
Try this:
function SaveContent() {
var myContent = document.getElementById("content")
var myLinks = myContent.getElementsByTagName('a')
while (myLinks.length) {
var child = myLinks[0];
var parent = child.parentNode;
var value = child.innerText;
parent.removeChild(child);
parent.innerText = value;
}
}
I have a table like this:
<table>
<thead>
<tr>
<th colspan="1">a</th>
<th colspan="3">b</th>
</tr>
</thead>
<tbody id="replaceMe">
<tr>
<td>data 1</td>
<td>data 2</td>
<td>data 3</td>
<td>data 4</td>
</tr>
</tbody>
</table>
and a method returns me the following after an ajax request:
<tr>
<td>data 1 new</td>
<td>data 2 new</td>
<td>data 3 new</td>
<td>data 4 new</td>
</tr>
I want to change the innerHTML like
document.getElementById('replaceMe').innerHTML = data.responseText;
However, it seems that IE can't set innerHTML on <tbody>. Can anyone help me with a simple workaround for this issue?
That is true, innerHTML on tbody elements is readOnly in IE
The property is read/write for all
objects except the following, for
which it is read-only: COL, COLGROUP,
FRAMESET, HEAD, HTML, STYLE, TABLE,
TBODY, TFOOT, THEAD, TITLE, TR.
source: http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
You can do something like this to work around it:
function setTBodyInnerHTML(tbody, html) {
var temp = tbody.ownerDocument.createElement('div');
temp.innerHTML = '<table>' + html + '</table>';
tbody.parentNode.replaceChild(temp.firstChild.firstChild, tbody);
}
Basically it creates a temporary node into which you inject a full table. Then it replaces the tbody with the tbody from the injected table. If it proves to be slow, you could make it faster by caching temp instead of creating it each time.
Create a temp node to store a table in, then copy them to the tbody
var tempNode = document.createElement('div');
tempNode.innerHTML = "<table>" + responseText+ "</table>";
var tempTable = tempNode.firstChild;
var tbody = // get a reference to the tbody
for (var i=0, tr; tr = tempTable.rows[i]; i++) {
tbody.appendChild(tr);
}
Both the answers above seem a bit unclear. Plus, the created div is never removed, so calling those functions repeatedly eats memory. Try this:
// this function must come before calling it to properly set “temp”
function MSIEsetTBodyInnerHTML(tbody, html) { //fix MS Internet Exploder’s lameness
var temp = MSIEsetTBodyInnerHTML.temp;
temp.innerHTML = '<table><tbody>' + html + '</tbody></table>';
tbody.parentNode.replaceChild(temp.firstChild.firstChild, tbody); }
MSIEsetTBodyInnerHTML.temp = document.createElement('div');
if (navigator && navigator.userAgent.match( /MSIE/i ))
MSIEsetTBodyInnerHTML(tbody, html);
else //by specs, you can not use “innerHTML” until after the page is fully loaded
tbody.innerHTML=html;
Even with this code, though, MSIE does not seem to properly re-size the table cells in my application, but I'm filling an empty tbody tag with variable generated content, while the thead cells' colspan values are set to a fixed value: the maximum number of cells that may be in the generated tbody. While the table tbody is 50 cells wide, only two columns show. Perhaps if the table was originally filled, and the cells were replaced with the same internal structure, this method would work. Google's Chrome does an excellent job of rebuilding the table, while Opera's desktop browser can resize to more columns just fine, but if you remove columns, the remaining column widths remain as narrow as they were; however with Opera, by hiding the table (display=none) then re-showing it (display=table), the generated table tbody cells then size properly. I've given up with Firefox. It's the MSIE-6 of 2012 - a nightmare to develop for which must have additional markup added just to make HTML-CSS layouts work because it does not conform to standards that even MSIE now does. So I haven't tested the tbody.innerHTML workings in Firefox.
This can be fixed by creating a shim/polyfill for .innerHTML. This could get you (you, dear reader) started:
if (/(msie|trident)/i.test(navigator.userAgent)) {
var innerhtml_get = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerHTML").get
var innerhtml_set = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerHTML").set
Object.defineProperty(HTMLElement.prototype, "innerHTML", {
get: function () {return innerhtml_get.call (this)},
set: function(new_html) {
var childNodes = this.childNodes
for (var curlen = childNodes.length, i = curlen; i > 0; i--) {
this.removeChild (childNodes[0])
}
innerhtml_set.call (this, new_html)
}
})
}
var mydiv = document.createElement ('div')
mydiv.innerHTML = "test"
document.body.appendChild (mydiv)
document.body.innerHTML = ""
console.log (mydiv.innerHTML)
http://jsfiddle.net/DLLbc/9/
<table id="table">
<thead>
<tr>
<th colspan="1">a</th>
<th colspan="3">b</th>
</tr>
</thead>
<tbody id="replaceMe">
<tr>
<td>data 1</td>
<td>data 2</td>
<td>data 3</td>
<td>data 4</td>
</tr>
</tbody>
</table>
<button type="button" onclick="replaceTbody()">replaceTbody</button>
<script>
function $(id){
return document.getElementById(id);
}
function replaceTbody(){
var table = $('table');
table.removeChild($('replaceMe'));
var tbody = document.createElement("tbody");
tbody.setAttribute("id","replaceMe");
table.appendChild(tbody);
var tr = document.createElement('tr');
for(var i=1;i<5;i++){
var td = document.createElement('td');
td.innerHTML = 'newData' + i;
tr.appendChild(td);
}
tbody.appendChild(tr);
}
</script>
I want reorder table rows using JavaScript .
for example take the following dummy table:
<table>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>A</td>
<td>B</td>
<td>C</td>
<td>D</td>
</tr>
<tr>
<td>A1</td>
<td>B1</td>
<td>C1</td>
<td>D1</td>
</tr>
</table>
I want to do this in JavaScript without using jQuery. I want to show the A1,B1,C1,D1.. row as the first row and then 1,2,3,4 row and then A,B,C,D row.
I know that there will be some wait time on the client side but I need to do it in the client side. Is there some generic solution to do this, for any number of rows?
If I understand correctly, you are asking how to take the last row and make it the first row, pushing down the rest. This should do it:
<table id="mytable">
...
</table>
<script type="text/javascript">
var tbl = document.getElementById('mytable');
var rows = tbl.getElementsByTagName('tr');
var firstRow = rows[0];
var lastRow = rows[rows.length];
firstRow.parentNode.insertBefore(lastRow.parentNode.removeChild(lastRow), firstRow);
</script>
Assuming your table does not have nested tables. At which point this would need to be a little smarter. This also assumes you're not using TBODY and THEAD nodes. But I'm sure you can get the idea and enhance it from there.
Do:
var table = ...; // Get reference to table (by ID or other means)
var lastRow = table.rows[table.rows.length - 1];
lastRow.parent.insertBefore(table.rows[0], lastRow);
The best way to solve this in Javascript is:
Give the Tr.. a unique name. for eg: X_Y,X_Z,A_Y,A_Z
Now add a hidden lable or text Box which gives the sorting order from the server i.e When the page renders I want to sort it All the Tr's that have a ID starting with A should come first and All the Z's should come second.
<asp:label id="lblFirstSortOrder" runat="server" style="display:none;">A,X</label>
<asp:label id="lblSecondSortOrder" runat="server" style="display:none;">Z,Y</label>
When the page renders..the order should be A_Z,A_Y,X_Z,X_Y
Before Rendering this is table that comes from the aspx file:
<table>
<tr id='Tr_Heading'>
<td>A</td>
<td>B</td>
</tr>
<tr id="Tr_X_Y">
<td>GH</td>
<td>GH1</td>
</tr>
<tr id="tr_X_Z">
<td>HU</td>
<td>HU1</td>
</tr>
<tr id="tr_A_Z">
<td>JI</td>
<td>JI1</td>
</tr>
<tr id="tr_A_Y">
<td>JI</td>
<td>JI1</td>
</tr>
Script:
function SortAndArrange()
{
var firstList = document.getElementById('lblFirstSortOrder').value;
var secondList = document.getElementById('lblSecondSortOrder').value;
var firstTypes = new Array();
firstTypes = firstList.split(',');
var secondLists = new Array();
secondLists = secondList.split(',');
var refNode = document.getElementById('Tbl_' + firstTypes[0] + "_" + secondTypes[0]);
for (var i = 0; i<firstTypes.length; i++)
{
for (var j = 0; j< secondTypes.length;j++)
{
var TrName = 'Tbl_'+firstTypes[i]+'_'+secondTypes[j];
var FirstSecondTrs = document.getElementById(TrName);
if (FirstSecondTrs)
{
FirstSecondTrs.parentNode.removeChild(FirstSecondTrs);
insertAfter(refNode,FirstSecondTrs);
refNode = FirstSecondTrs;
}
}
}
}
function insertAfter( referenceNode, newNode )
{
referenceNode.parentNode.insertBefore( newNode, referenceNode.nextSibling );
}
I hope you guys get the idea.. for me the sorting order will always come from the server and not from the user of the page...
Thanks a Lot for all the answers.. Apprecite it. Helped me get to this solution.
Thanks,
Ben