I found a function to move data between table cells but the functions never seem to stick to whatever tag is "attached" to the cell itself. I'm not sure if I was using ids wrong. I need help finding a way to "attach" a function to a tag that moves between cells.
Can you help me create a button to move a tag (unit 1) upwards and downwards through a table such that it stops at the end of the table?
Original code attached here
//Send to the "bottom"
function sendOS() {
var node = document.getElementById("r1c1").lastChild;
document.getElementById("r1c3").appendChild(node);
/*
var node = document.getElementById("r1c3").lastChild;
document.getElementById("r1c2").appendChild(node);
*/
}
//Send to the "top"
function sendTop() {
var node = document.getElementById("r1c2").lastChild;
document.getElementById("r1c1").appendChild(node);
}
table,
th,
td {
border: 1px solid black;
width: 32px;
height: 32px;
}
<table>
<tr id="row1">
<td id="r1c1">Unit1</th>
<td id="r1c2">Unit2</th>
<td id="r1c3">Unit3</th>
</tr>
<tr id="row2">
<td id="r2c1">r1c2</td>
<td id="r2c2">r2c2</td>
<td id="r2c2">r2c3</td>
</tr>
<tr id="row3">
<td id="r2c2">r3c1</td>
<td id="r2c2">r3c2</td>
<td id="r2c2">r3c3</td>
</tr>
</table>
<!--Table ends -->
<!-------------------------------------------------------------->
<button onclick="sendOS()">move to the other side</button>
<button onclick="sendTop()">move to the right</button>
I can't help you with creating all that. You should try it out for yourself, you'll learn a lot more.
But I can help you with the code you provided.
A lot of your id attributes are the same. Every id on the page should be unique.
Change your HTML structure slightly by adding a <span> around the texts inside your cells (<td><span id="target-1">Text</span></td>). That way you can select elements inside your cells and move those around. Using lastChild to get the TextNode is not the right approach.
The functions should check if there is a cell next to it in the direction you want to move the text. If there is, move the text. If not, then do nothing.
Below I've made a small demonstration on how this might work. Here target is the element that we move. It's the <span id="target">Foo</span> element in the HTML.
When clicking either button, the code will go one element up from the target element with parentElement, that will be the <td> our target is in.
It then tries to access the previous or next <td> in the row (depending on the direction). If a neighbouring <td> is found, it will append the element.
The advantage of this approach is that you always have a reference to the element that you are moving.
const target = document.querySelector('#target');
const buttonLeft = document.querySelector('.js-target-move-left');
const buttonRight = document.querySelector('.js-target-move-right');
function targetMoveLeft() {
const previousCell = target.parentElement.previousElementSibling;
if (previousCell) {
previousCell.append(target);
}
}
function targetMoveRight() {
const nextCell = target.parentElement.nextElementSibling;
if (nextCell) {
nextCell.append(target);
}
}
buttonLeft.addEventListener('click', targetMoveLeft);
buttonRight.addEventListener('click', targetMoveRight);
<table>
<tbody>
<tr>
<td><span id="target">Foo</span></td>
<td><span>Bar</span></td>
<td><span>Baz</span></td>
</tbody>
</table>
<button class="js-target-move-left">Left</button>
<button class="js-target-move-right">Right</button>
I hope this will at least push you in the right direction. Good luck with implementing the other features.
Related
I don't know why something this simple isn't working.
<table id="df">
<tr><td>foo</td></tr>
<tr><td>bar</td></tr>
<tr><td>abc#yahoo.com</td></tr>
</table>
Say we have an HTML table. How would I hide all rows / only some rows, and vice versa, how would I show all rows / only some rows?
Things that aren't working:
//one
document.getElementByTagName('table').style.display = "none";
//two
var rows = document.getElementsByTagName("table")[0].rows;
rows.hidden = false;
//three
var table = document.getElementById("df");
table.style = "display:table-row";
Thanks in advance
You can select row n using nth-child css selector
const table = document.querySelector("#df tbody")
const toggleRow = (rowIndex = 0) => {
if (table.childElementCount < rowIndex) return
const row = table.querySelector(`:nth-child(${rowIndex})`)
row.style.display = row.style.display === 'none' ? 'block' : 'none'
}
toggleRow(1) // Hide first row
toggleRow(2) // Hide second row
toggleRow(4) // Will not be hidden because table only have 3 rows
toggleRow(2) // Show second row
<table id="df">
<tbody>
<tr>
<td>foo</td>
</tr>
<tr>
<td>bar</td>
</tr>
<tr>
<td>abc#yahoo.com</td>
</tr>
</tbody>
</table>
First, the problems:
document.getElementByTagName('table').style.display = "none";
getElementByTagName() doesn't exist; this will have generated an error and would have been shown to you in the developer console had you looked. While getElementsByTagName() exists, the existence of a means to get all elements of a given means (id,tag-name, class...) doesn't imply that a similar method exists to get a singular element via the same approach.
This didn't work because it couldn't work (unless you created the function yourself).
var rows = document.getElementsByTagName("table")[0].rows;
rows.hidden = false;
This couldn't work because while document.getElementsByTagName('table')[0] would return the first <table> in the document, HTMLTableElement.rows property returns a NodeList over which you would have to iterate to hide each row in turn. Again, this would have generated an error which you should have seen in your developer console.
Further, setting rows.hidden to false means that even if it had worked, and assigned the false value to the hidden property of the collection of elements, they would still be visible because you tried to set their hidden to false (which means they would be visible), instead of true which would hide them.
var table = document.getElementById("df");
table.style = "display:table-row";
I don't honestly know what you were trying to achieve with this, but displaying the <table> as if it was a <tr> was never going to hide the <table>, just make it look like something else.
const hideMethods = {
// here we map the 'data-demo' attribute-value of each <button> to a particular function:
getElementsByTagName: (e) =>
// we navigate from the Event Object's currentTarget (the element to which the initiating
// function was bound) to the closest ancestor <section> element:
e.currentTarget.closest('section')
// within that <section> we use getElementsByTagName() to retrieve all <table> elements,
// and then supply the zeroeth index to get the first:
.getElementsByTagName('table')[0]
// we then update the 'display' property of the CSSStyleDeclaration object, setting it to
// 'none' to have it be hidden:
.style.display = "none",
// here we use the 'hidden' property of various nodes:
hidden: (e) =>
// again, navigating to the ancestor <section>:
e.currentTarget.closest('section')
// using Element.querySelectorAll() to find all <tr> elements inside of <table> elements:
.querySelectorAll('table tr')
// and iterating over each of those <tr> elements in turn, using NodeList.prototype.forEach():
.forEach(
// we use an Arrow function to pass a reference to the current <tr> of the NodeList over
// which we're iterating, and within the function body we update the
// HTMLElement.hidden property, setting it to true, in order to hide it:
(row) => row.hidden = true),
querySelector: (e) =>
// navigating to the ancestor <section>:
e.currentTarget.closest('section')
// using Element.querySelector to retrieve the first/only <table> within the <section>
.querySelector('table')
// updating the HTMLElement.hidden property to hide the element (along with its
// descendants):
.hidden = true
},
showMethods = {
// here we map the 'data-demo' attribute-value of each <button> to a particular function:
getElementsByTagName: (e) =>
// we navigate from the Event Object's currentTarget (the element to which the initiating
// function was bound) to the closest ancestor <section> element:
e.currentTarget.closest('section')
// within that <section> we use getElementsByTagName() to retrieve all <table> elements,
// and then supply the zeroeth index to get the first:
.getElementsByTagName('table')[0]
// we then update the 'display' property of the CSSStyleDeclaration object, setting it to
// 'block' to have it be visible:
.style.display = "",
// here we use the 'hidden' property of various nodes:
hidden: (e) =>
// again, navigating to the ancestor <section>:
e.currentTarget.closest('section')
// using Element.querySelectorAll() to find all <tr> elements inside of <table> elements:
.querySelectorAll('table tr')
// and iterating over each of those <tr> elements in turn, using NodeList.prototype.forEach():
.forEach(
// we use an Arrow function to pass a reference to the current <tr> of the NodeList over
// which we're iterating, and within the function body we update the
// HTMLElement.hidden property, setting it to false, in order to show it:
(row) => row.hidden = false),
querySelector: (e) =>
// navigating to the ancestor <section>:
e.currentTarget.closest('section')
// using Element.querySelector to retrieve the first/only <table> within the <section>
.querySelector('table')
// updating the HTMLElement.hidden property to show the element (along with its
// descendants):
.hidden = false,
},
// simple function which takes an element node:
getIndex = (el) => {
// caches the parentNode of that element node:
let parent = el.parentNode,
// retrieves the iterable children NodeList from
// the parent, and converts it into an Array:
children = [...parent.children];
// returns the result of calling Array.prototype.indexOf()
// on that Array looking for the passed-in element:
return children.indexOf(el);
};
// retrieving all <button> elements in the document, and iterating over that collection
// with NodeList.prototype.forEach():
document.querySelectorAll('button.close').forEach(
// Arrow function passing a reference to the current <button> into the function, in
// which we use EventTarget.addEventListener() to bind the anonymous function as
// the event-handler for the 'click' event on the <button>:
(btn) => btn.addEventListener('click', (e) =>
// here we call the functions defined in the hideMethods Object:
hideMethods[
// retrieving the function-name from the 'data-demo' attribute, using
// the Element.dataset API
btn.dataset.demo
// passing a reference to the outer Event Object to the function:
](e))
)
// retrieving all <button> elements in the document, and iterating over that collection
// with NodeList.prototype.forEach():
document.querySelectorAll('button.show').forEach(
// Arrow function passing a reference to the current <button> into the function, in
// which we use EventTarget.addEventListener() to bind the anonymous function as
// the event-handler for the 'click' event on the <button>:
(btn) => btn.addEventListener('click', (e) =>
// here we call the functions defined in the showMethods Object:
showMethods[
// retrieving the function-name from the 'data-demo' attribute, using
// the Element.dataset API
btn.dataset.demo
// passing a reference to the outer Event Object to the function:
](e))
)
document.querySelectorAll('table button.hide').forEach(
(btn) => btn.addEventListener('click', (e)=> btn.closest('tr').hidden = true)
);
// retrieving all <button> elements with a class of 'hide' inside of an ancestor
// element with a class of 'controls', and iterating over that collection with
// NodeList.prototype.forEach():
document.querySelectorAll('.controls button.hide').forEach(
// passing the current button element into the function bocy; and binding the
// anonymous function of EventTarget.addEventListener() as the event-handler
// for the 'click' event:
(btn) => btn.addEventListener('click', (e)=> {
// caching a reference to the <tr> element, which was found by navigating
// to the closest <section> ancestor, finding the first/only <table>
// element inside that <section> and accessing the HTMLTableElement.rows
// colleaction, passing in the current index of the <button> from amongst
// its own siblings to retrieve a <tr> element of the same index in its
// own sibling collection:
let row = btn.closest('section').querySelector('table').rows[ getIndex(btn) ],
// caching the current state since we're using a toggle function:
currentState = row.hidden;
// setting the HTMLElement.hidden property to the inverse of its current
// state using the negation operator:
row.hidden = !currentState;
})
);
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
main {
display: grid;
gap: 0.5em;
grid-template-columns: repeat(auto-fit, 13em);
inline-size: clamp(13em, 80vw, 1200px);
margin-block: 1em;
margin-inline: auto;
}
section {
border: 1px solid #000;
display: flex;
flex-flow: column nowrap;
justify-content: space-between;
gap: 0.5em;
padding: 0.5em;
}
h2 {
font-size: 1.1em;
font-style: italic;
font-weight: 400;
}
<main>
<section>
<h2>Hide the table</h2>
<button type="button" class="show" data-demo="getElementsByTagName">Show with 'getElementsByTagName'</button>
<table class="df">
<tr>
<td>foo</td>
</tr>
<tr>
<td>bar</td>
</tr>
<tr>
<td>abc#yahoo.com</td>
</tr>
</table>
<button type="button" class="close" data-demo="getElementsByTagName">Hide with 'getElementsByTagName()''</button>
</section>
<section>
<h2>Hide the table</h2>
<button type="button" class="show" data-demo="hidden">Show with 'hidden'</button>
<table class="df">
<tr>
<td>foo</td>
</tr>
<tr>
<td>bar</td>
</tr>
<tr>
<td>abc#yahoo.com</td>
</tr>
</table>
<button type="button" class="close" data-demo="hidden">Hide with 'hidden'</button>
</section>
<section>
<h2>Hide the table</h2>
<button type="button" class="show" data-demo="querySelector">Show with 'hidden'</button>
<table class="df">
<tr>
<td>foo</td>
</tr>
<tr>
<td>bar</td>
</tr>
<tr>
<td>abc#yahoo.com</td>
</tr>
</table>
<button type="button" class="close" data-demo="querySelector">Hide with 'hidden'</button>
</section>
<section>
<h2>Hide rows</h2>
<table class="df">
<tr>
<td>foo</td><td><button type="button" class="hide">X</button></td>
</tr>
<tr>
<td>bar</td><td><button type="button" class="hide">X</button></td>
</tr>
<tr>
<td>abc#yahoo.com</td><td><button type="button" class="hide">X</button></td>
</tr>
</table>
</section>
<section>
<h2>Toggle rows</h2>
<table class="df">
<tr>
<td>foo</td>
</tr>
<tr>
<td>bar</td>
</tr>
<tr>
<td>abc#yahoo.com</td>
</tr>
</table>
<div class="controls">
<button type="button" class="hide">1</button>
<button type="button" class="hide">2</button>
<button type="button" class="hide">3</button>
</div>
</section>
</main>
JS Fiddle demo.
References:
document.querySelector().
document.querySelectorAll().
Element.querySelector().
Element.querySelectorAll().
Element.closest().
HTMLElement.dataset API.
HTMLElement.hidden.
HTMLTableElement.rows.
try this way...
<table id="df">
<tr id="row_1"><td>foo</td></tr>
<tr id="row_2"><td>bar</td></tr>
<tr id="row_3"><td>abc#yahoo.com</td></tr>
</table>
Hide all rows:
const tableRows = document.querySelectorAll('#df tr');
tableRows.forEach(tr => tr.style.display = 'none');
Hide some row:
const rowOne = document.getElementById('row_1');
rowOne.style.display = 'none';
Actually there is some mistake in your spelling.
try this
document.getElementsByTagName("table");
document.getElementsByTagName("table")[0].rows
I am new to programming and am mostly just playing around with HTML and JavaScript right now. I recently learned about jQuery and was trying to use it in a periodic table quiz page I made. I wanted to be able to mouse over certain elements and see a relevant picture and then mouse off and restore it to the element symbol and name that was there before. Here is my code:
The element is in a table coded by HTML, like so:
<table id="ptable" style="text-align:center;">
<tr>
<td>
<div id="einsteinium"><span style="font-size:32px;">Es</span>
<p style="font-size:12px;">Einsteinium</p>
</div>
</td>
<td>hydrogen</td>
<td>helium</td>
</tr>
</table>
And here is the jQuery:
$(document).ready(function () {
var oldhtml = "";
oldhtml = $("#einsteinium").html();
$("#einsteinium").hover(function () {
$("#einsteinium").html("<img src=\"http://images.mentalfloss.com/sites/default/files/styles/insert_main_wide_image/public/einstein1_7.jpg\" height=\"70px\" width=\"70px\">");
},
function () {
$("#einsteinium").html(oldhtml);
});
});
Fiddle
The problem is that the picture of Einstein will get stuck and won't return to the element symbol/name. On the fiddle, if you keep mousing over it, it will start working again, but it doesn't do that on my code. For me, when it gets stuck it doesn't get unstuck (but the fiddle does get stuck too, and I don't want that to happen at all). I have tried changing the z-index of the div and table, but no luck there. I'd really appreciate any help!
The problem is because, you are changing the contents of the hovering div, which is messing up the mouse enter/leave events
You can do it without any javascript, but with css and the :hover selector like
#einsteinium > img {
display: none;
}
#einsteinium:hover > img {
display: inline-block;
}
#einsteinium:hover > div {
display: none;
}
<table id="ptable" style="text-align:center;">
<tr>
<td>
<div id="einsteinium">
<img src="http://images.mentalfloss.com/sites/default/files/styles/insert_main_wide_image/public/einstein1_7.jpg" height="70px" width="70px">
<div>
<span style="font-size:32px;">Es</span>
<p style="font-size:12px;">Einsteinium</p>
</div>
</div>
</td>
<td>hydrogen</td>
<td>helium</td>
</tr>
</table>
Hi,
I need help with a class assignment. We were given an HTML file for a pretend business, "Castaway Vacations LLC". The first step in the assignment is to create a javascript function that changes the Text Color, Hyperlink Color, and Image when a particular link is clicked. So when the "Romantic" link is clicked, for example, the text that reads "Select Mood" will change to red, along with all other text on the page (including the other hyperlinks). Clicking this link will also change the image. One thing that's tricky about the file that our teacher sent is that there isn't a matching CSS file - I created one myself that contains the class colors, but besides that, all the other styles are inline, which I'm not used to. Here is the JSfiddle link to my code:
UPDATE!
I've gotten the code down for changing text color and image, but the hyperlink colors still aren't changing. You can see my attempt at changing them with the function named colorLinks in the updated javascript. Unfortunately, not only does this function not work, it's also causing the previous (good) functions to not work as well. Thanks for your help.
http://jsfiddle.net/HappyHands31/twkm12r2/1/
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Castaway Vacations, LLC</title>
</head>
<body leftmargin=0 rightmargin=0 bgcolor=#ffcc99
text=#993300 link=#993300 vlink=#996633>
<br>
<table width=100% border=0 cellpadding=0 cellspacing=0>
<tr>
<td width=95% align="right" bgcolor=#ffffff>
<img src="castaway_logo.jpg">
<br>
<font face=arial>Vacations, LLC</font></td>
<td bgcolor=#ffffff> </td>
</tr>
</table>
<br><br>
<div align="center">
<table width=600>
<tr>
<td width=300 valign="top">
<font face=arial size=3><b><i>Select Mood...</i></b></font><br><br>
<font face=arial>
<a id="one" href="#">Romantic</a><br><br>
<a id="two" href="#">Adventure</a><br><br>
<a id="three" href="#">Relaxation</a><br><br>
<a id="four" href="#">Family</a><br><br><br>
<br>
Request A Brochure...
</font>
</td>
<td align="center"><img id="original.jpg" src="orig_main.jpg">
<br> <i>Your Vacation Awaits!
</tr>
</center>
<script src="castaway.js"></script>
</body>
</html>
</DOCTYPE>
Javascript:
document.getElementById('one').addEventListener
('click', change_color);
document.getElementById('two').addEventListener
('click', change_color2);
document.getElementById('three').addEventListener
('click', change_color3);
document.getElementById('four').addEventListener
('click', change_color4);
function change_color(){
document.body.style.color = "red";
document.getElementById("original.jpg").src = "rom_main.jpg";
}
function change_color2(){
document.body.style.color = "blue";
document.getElementById("original.jpg").src = "adv_main.jpg";
}
function change_color3(){
document.body.style.color = "green";
document.getElementById("original.jpg").src = "rel_main.jpg";
}
function change_color4(){
document.body.style.color = "orange";
document.getElementById("original.jpg").src = "fam_main.jgp";
}
colorLinks ("#00FF00");
function colorLinks (hex)
var links = document.getElementsByTagName("a");
for(var i=0;i<links.length;i++)
{
if(links[i].href)
{
links[i].style.color = "red";
}
}
Thank you!
ok, for starters to change color of something you just need to do something like this
function change_color(){
document.body.style.color = "red"; // this will change the color of the text to RED
document.getElementById("image_to_change").src = "something.jpg"; // change img src
}
to use the function just add the call to the function where you want it, for example
<a onClick="change_color();" href="#">Romantic</a><br><br>
well, its already accepted answer, but just wanted to complete it, to change the img src of, first you need to add an id for that img, for example
<td align="center"><img id="image_to_change"src="orig_main.jpg"><br><i>Your Vacation Awaits!
after you added the id, you can see how i added this line to the "change_color" function
document.getElementById("image_to_change").src = "***SOURCE_TO_YOUR_IMAGE***";
* Working Code *
OK, here is the working fiddle http://jsfiddle.net/8ntdbek3/1/
i changed a few things to make it work, first, you need to understand that when you give an "ID" to something, in this case to the img (you gave the id "rom_main.jpg") the ID is what you need to use to call that element, so when you are using
document.getElementById("orig_main.jpg").src = "rom_main.jpg";
it should go
document.getElementById("rom_main.jpg").src = "rom_main.jpg";
since you gave it the id "rom_main.jpg" in this line, and the id "orig_main.jpg" does not exist!
<td align="center"><img id=**"rom_main.jpg**" src="orig_main.jpg"><br><i>Your Vacation Awaits!
also its important to note that you can put ANY id that you want, so repeating the name of the jpg file its something that i would not recommend, since it leads to confusion.
I changed the ID to something more readable and thats it. if you still need help comment below and i'll do my best to help you.
* EDIT to change color *
ok, finally i added a few lines of code to change the color of EVERY element in your page, for each onclick i adde these lines:
var list = document.getElementsByTagName("a");
for (i = 0; i < list.length; i++) {
list[i].style.color = "red";
}
as you can see, i get in a list every element with the tag "a", then every element i turn it into "red" (or the color you want).
here is the working fiddle http://jsfiddle.net/8ntdbek3/3/
regards.
I am currently attempting to append a specific , via jquery, to another table. Here's the HTML, and the two elements involved in the move.
<div id="content_area">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr><td></td></tr>
<tr>
<td></td> <-- TD needing to move -->
</tr>
</tbody>
</table> <-- Needs to move-->
<table width="100%" cellspacing="0" cellpadding="5" border="0">
<tbody>
<tr>
<td width="190" valign="top">
<table width="100%"></table>
<-- Move Above TD Here -->
</td>
</tr>
</tbody>
</table>
</div>
Although I'm hardly experienced with jquery/javascript, I have used the following method in the past to append a div to another div.
$(".target").appendTo($(".destination"));
This method I have used in the past required that the elements have some sort of unique identification. Since this is not possible with the current site I am developing (The software has locked down the HTML), how can I target these two tables in order to make the append?
You can view the issue at the following page:
http://xlevj.jyetp.servertrust.com/Pro-Audio-Equipment-s/1824.htm
It's pretty obvious to see on that page what I'm trying to accomplish with the move. Thanks for any help!
Try this:
//Find the td we want to move.
var tdToMove = $('#divWaitModal + table > tbody > tr:nth-child(2) td');
//Find the td we want to insert into after.
var tdToInsertAfter = $('#divWaitModal + table + table tr:first-child td:first-child');
//Detach the td to move.
tdToMove.detach();
//Insert it at the proper place.
tdToInsertAfter.after(tdToMove);
Just use child number of the node and target trough that :
$('body table:first-child').appendTo( $('table:eq(1) td:eq(0)') );
In words it takes the first table and it's appending it to second table > first cell. You can use :eq( number ) where number starts from 0, or first-child selector in some cases ..
This CSS might accomplish what you're after:
#content_area {
overflow: hidden;
}
#content_area table {
display: inline;
float: left;
}
If you want to target the elements, you can use the #content_area as a selector:
var $tables = $('#content_area>table');
var $table1 = $(tables[0]);
var $table2 = $(tables[1]);
I am dynamically generating a div which is like :
<div id='PrintDiv'>
<table id="mainTable">
<tr>
<td>
Col1
</td>
<td>
Col2
</td>
<td>
Col3
</td>
</tr>
<tr>
<td>
Val1
</td>
<td>
Val2
</td>
<td>
Val3
</td>
</tr>
<tr>
<td>
Val11
</td>
<td>
Val22
</td>
<td>
Val33
</td>
</tr>
<tr>
<td>
Val111
</td>
<td>
Val222
</td>
<td>
Val333
</td>
</tr>
</table>
</div>
And there are lot more elements on the page as well.
Now, how can i get a csv file like this :
Col1,Col2,Col3
Val1,Val2,Val3
Val11,Val22,Val33
Val111,Val222,Val333
using jQuery ?
need a file save dailog box too,like this :
Thanks.
You can do that in the client side only, in browser that accept Data URIs:
data:application/csv;charset=utf-8,content_encoded_as_url
In your example the Data URI must be:
data:application/csv;charset=utf-8,Col1%2CCol2%2CCol3%0AVal1%2CVal2%2CVal3%0AVal11%2CVal22%2CVal33%0AVal111%2CVal222%2CVal333
You can call this URI by:
using window.open
or setting the window.location
or by the href of an anchor
by adding the download attribute it will work in chrome, still have to test in IE.
To test, simply copy the URIs above and paste in your browser address bar. Or test the anchor below in a HTML page:
<a download="somedata.csv" href="data:application/csv;charset=utf-8,Col1%2CCol2%2CCol3%0AVal1%2CVal2%2CVal3%0AVal11%2CVal22%2CVal33%0AVal111%2CVal222%2CVal333">Example</a>
To create the content, getting the values from the table, you can use table2CSV and do:
var data = $table.table2CSV({delivery:'value'});
$('<a></a>')
.attr('id','downloadFile')
.attr('href','data:text/csv;charset=utf8,' + encodeURIComponent(data))
.attr('download','filename.csv')
.appendTo('body');
$('#downloadFile').ready(function() {
$('#downloadFile').get(0).click();
});
Most, if not all, versions of IE don't support navigation to a data link, so a hack must be implemented, often with an iframe. Using an iFrame combined with document.execCommand('SaveAs'..), you can get similar behavior on most currently used versions of IE.
This is my implementation (based in: https://gist.github.com/3782074):
Usage:
HTML:
<table class="download">...</table>
DOWNLOAD CSV
JS:
$("a[download]").click(function(){
$("table.download").toCSV(this);
});
Code:
jQuery.fn.toCSV = function(link) {
var $link = $(link);
var data = $(this).first(); //Only one table
var csvData = [];
var tmpArr = [];
var tmpStr = '';
data.find("tr").each(function() {
if($(this).find("th").length) {
$(this).find("th").each(function() {
tmpStr = $(this).text().replace(/"/g, '""');
tmpArr.push('"' + tmpStr + '"');
});
csvData.push(tmpArr);
} else {
tmpArr = [];
$(this).find("td").each(function() {
if($(this).text().match(/^-{0,1}\d*\.{0,1}\d+$/)) {
tmpArr.push(parseFloat($(this).text()));
} else {
tmpStr = $(this).text().replace(/"/g, '""');
tmpArr.push('"' + tmpStr + '"');
}
});
csvData.push(tmpArr.join(','));
}
});
var output = csvData.join('\n');
var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(output);
$link.attr("href", uri);
}
Notes:
It uses "th" tags for headings. If they are not present, they are not
added.
This code detects numbers in the format: -####.## (You will need modify the code in order to accept other formats, e.g. using commas).
UPDATE:
My previous implementation worked fine but it didn't set the csv filename. The code was modified to use a filename but it requires an < a > element. It seems that you can't dynamically generate the < a > element and fire the "click" event (perhaps security reasons?).
DEMO
http://jsfiddle.net/nLj74t0f/
(Unfortunately jsfiddle fails to generate the file and instead it throws an error: 'please use POST request', don't let that error stop you from testing this code in your application).
I recently posted a free software library for this: "html5csv.js" -- GitHub
It is intended to help streamline the creation of small simulator apps
in Javascript that might need to import or export csv files, manipulate, display, edit
the data, perform various mathematical procedures like fitting, etc.
After loading "html5csv.js" the problem of scanning a table and creating a CSV is a one-liner:
CSV.begin('#PrintDiv').download('MyData.csv').go();
Here is a JSFiddle demo of your example with this code.
Internally, for Firefox/Chrome this is a data URL oriented solution, similar to that proposed by #italo, #lepe, and #adeneo (on another question). For IE
The CSV.begin() call sets up the system to read the data into an internal array. That fetch then occurs. Then the .download() generates a data URL link internally and clicks it with a link-clicker. This pushes a file to the end user.
According to caniuse IE10 doesn't support <a download=...>. So for IE my library calls navigator.msSaveBlob() internally, as suggested by #Manu Sharma
Here are two WORKAROUNDS to the problem of triggering downloads from the client only. In later browsers you should look at "blob"
1. Drag and drop the table
Did you know you can simply DRAG your table into excel?
Here is how to select the table to either cut and past or drag
Select a complete table with Javascript (to be copied to clipboard)
2. create a popup page from your div
Although it will not produce a save dialog, if the resulting popup is saved with extension .csv, it will be treated correctly by Excel.
The string could be
w.document.write("row1.1\trow1.2\trow1.3\nrow2.1\trow2.2\trow2.3");
e.g. tab-delimited with a linefeed for the lines.
There are plugins that will create the string for you - such as http://plugins.jquery.com/project/table2csv
var w = window.open('','csvWindow'); // popup, may be blocked though
// the following line does not actually do anything interesting with the
// parameter given in current browsers, but really should have.
// Maybe in some browser it will. It does not hurt anyway to give the mime type
w.document.open("text/csv");
w.document.write(csvstring); // the csv string from for example a jquery plugin
w.document.close();
DISCLAIMER: These are workarounds, and does not fully answer the question which currently has the answer for most browser: not possible on the client only
By using just jQuery, you cannot avoid a server call.
However, to achieve this result, I'm using Downloadify, which lets me save files without having to make another server call. Doing this reduces server load and makes a good user experience.
To get a proper CSV you just have to take out all the unnecessary tags and put a ',' between the data.
You can't avoid a server call here, JavaScript simply cannot (for security reasons) save a file to the user's file system. You'll have to submit your data to the server and have it send the .csv as a link or an attachment directly.
HTML5 has some ability to do this (though saving really isn't specified - just a use case, you can read the file if you want), but there's no cross-browser solution in place now.
Hope the following demo can help you out.
$(function() {
$("button").on('click', function() {
var data = "";
var tableData = [];
var rows = $("table tr");
rows.each(function(index, row) {
var rowData = [];
$(row).find("th, td").each(function(index, column) {
rowData.push(column.innerText);
});
tableData.push(rowData.join(","));
});
data += tableData.join("\n");
$(document.body).append('<a id="download-link" download="data.csv" href=' + URL.createObjectURL(new Blob([data], {
type: "text/csv"
})) + '/>');
$('#download-link')[0].click();
$('#download-link').remove();
});
});
table {
border-collapse: collapse;
}
td,
th {
border: 1px solid #aaa;
padding: 0.5rem;
text-align: left;
}
td {
font-size: 0.875rem;
}
.btn-group {
padding: 1rem 0;
}
button {
background-color: #fff;
border: 1px solid #000;
margin-top: 0.5rem;
border-radius: 3px;
padding: 0.5rem 1rem;
font-size: 1rem;
}
button:hover {
cursor: pointer;
background-color: #000;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='PrintDiv'>
<table id="mainTable">
<tr>
<td>Col1</td>
<td>Col2</td>
<td>Col3</td>
</tr>
<tr>
<td>Val1</td>
<td>Val2</td>
<td>Val3</td>
</tr>
<tr>
<td>Val11</td>
<td>Val22</td>
<td>Val33</td>
</tr>
<tr>
<td>Val111</td>
<td>Val222</td>
<td>Val333</td>
</tr>
</table>
</div>
<div class="btn-group">
<button>csv</button>
</div>
Just try the following coding...very simple to generate CSV with the values of HTML Tables. No browser issues will come
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="http://www.csvscript.com/dev/html5csv.js"></script>
<script>
$(document).ready(function() {
$('table').each(function() {
var $table = $(this);
var $button = $("<button type='button'>");
$button.text("Export to CSV");
$button.insertAfter($table);
$button.click(function() {
CSV.begin('table').download('Export.csv').go();
});
});
})
</script>
</head>
<body>
<div id='PrintDiv'>
<table style="width:100%">
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>80</td>
</tr>
</table>
</div>
</body>
</html>