0

I have a table with thead, tfoot, tr, td and th inside it. The td or th has colspan or rowspan attribute, how to convert it to csv string with every merged cells unmerged as multiple fields?

for example:

<table>
  <thead>
    <tr>
      <th>1</th>
      <th>2</th>
    </tr>
  </thead>
  <tr>
    <td rowspan='2'>3</td>
    <td>4</td>
  </tr>
  <tr>
    <td>5</td>
  </tr>
  <tfoot>
    <tr>
      <th colspan='2'>6 <span style='display:none'> 7 
</span> 8</th>
    </tr>
  </tfoot>
</table>

should be outputting:

"1","2"
"3","4"
"","5"
"6  7  8",""

Is there any other plugin other than table2CSV? because this one displaying tfoot on the first row instead on the last row, also merged cells shown as single field.

Kokizzu
  • 24,974
  • 37
  • 137
  • 233
  • You can always do it manually. And there is _Questions asking us to recommend or find a book, tool, software library, tutorial or other off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it._ in "close question" reasons. – Regent Nov 07 '14 at 13:05
  • explore here http://stackoverflow.com/questions/15547198/export-html-table-to-csv – ankit.jbp Nov 07 '14 at 13:16
  • yes, but all those solution using table2CSV, that has a bug that I mention on the first line @ankit.jbp – Kokizzu Nov 07 '14 at 13:20

2 Answers2

1

I have made a plugin 100% jQuery-free.

You can integrate it with jQuery in no time.

The goal was to try to make this work in the best possible way.

Here is the code:

(function(window,undefined){

    window.T2CSV=function(table){
        if(! (table instanceof window.HTMLTableElement))
        {
            throw new window.TypeError('A <table> element is required, instead '+table+' was passed');
        }

        var tr,thead,csv,tfoot,cols,prop=(table.innerText===undefined?'textContent':'innerText'),
            setVars=function(){
                var elements=table.getElementsByTagName('tr');

                if(elements.length<1)
                {
                    throw new window.RangeError('At least 1 <tr> element is required, you have 0 on your <table>.');
                }

                tr=Array.prototype.slice.call(elements,1);
                thead=elements[0];
                cols=thead.children.length;
                elements=null; //free memory
                csv='';

            },
            render={
                header:function(){
                    if(! (thead.children[0] instanceof window.HTMLTableCellElement))
                    {
                        throw new window.RangeError('At least 1 <tr> element with 1 <td> or <th> is required.');
                    }

                    for(var i=0,children=thead.children,l=children.length,csv=[];i<l;i++)
                    {
                        csv[csv.length]='"'+children[i][prop]+'"';
                    }
                    children=null; //free memory
                    return csv;
                },
                data:function(){

                    if(!tr.length)
                    {
                        return '';
                    }

                    for(var i=0,l=tr.length,csv=[],tfoot=false;i<l;i++)
                    {
                        if(!tfoot && tr[i].parentNode.tagName=='TFOOT')
                        {
                            tfoot=tr[i];
                            continue;
                        }
                        csv[csv.length]=render.row(tr[i]);
                    }

                    if(tfoot)
                    {
                        csv[csv.length]=render.row(tfoot);
                    }

                    return csv.join('\r\n');
                },
                row:function(tr){
                    var td=tr.getElementsByTagName('td');

                    if(!td.length)
                    {
                        td=tr.getElementsByTagName('th');
                    }

                    for(var i=0,tmp=[];i<cols;i++)
                    {
                        tmp[i]=td[i]?'"'+td[i][prop]+'"':'""';
                    }
                    return tmp+'';
                }
            };

        setVars();

        return {
            toString:function(){
                if(csv)
                {
                    return csv;
                }

                return csv = [render.header(),render.data()].join('\r\n');
            },
            valueOf:function(){return this.toString();},
            refresh:function(){
                setVars();
            }
        }

    }

})(function(){}.constructor('return this')());

This: function(){}.constructor('return this')() is a beautiful trick inspired by JSFuck, which returns the REAL window objects AT ALL TIMES!
Check here for the source: http://www.jsfuck.com/

It lacks comments, but I'm pretty sure that what I'm doing is easy to understand.

If I'm wrong, drop a comment and I will make this easier to understand.

The usage is simple: just pass a table (a pure table, no jQuery in it or it will choke) and convert it to a string.

The generated csv is cached, so, multiple accesses are really fast.

The method .refresh() will destroy that cache.

This isn't the most efficient method, but it works.

For obvious reasons, the output will be a bit different.

Instead of this:

"1","2"
"3","4"
"","5"
"6  7  8",""

It produces this:

"1","2"
"3","4"
"5",""
"6  7  8",""

The code can be easily customized.

I'm thinking about updating it and adding escapes for the separators and configurations.

Check it here in action: http://jsfiddle.net/qw8ponhu/2/


Update (15/11/2014):

I have made an improved version!

It now escapes slashes and double-quotes.

I still haven't added support for more delimiters and text quotes.

Here is the code I have:

(function(window,undefined){
    window.T2CSV=function(table){
        if(!(table instanceof window.HTMLTableElement))
        {
            throw new window.TypeError('A <table> element is required, instead '+table+' was passed');
        }

        var tr,thead,cols,tfoot,csv={
                header:'',
                data:[],
                footer:'',
                string:''
            },
            prop=(table.innerText===undefined?'textContent':'innerText'),
            setVars=function(){
                var elements=table.getElementsByTagName('tr');

                if(elements.length<1)
                {
                    throw new window.RangeError('At least 1 <tr> element is required, you have 0 on your <table>.');
                }

                tr=Array.prototype.slice.call(elements,1);
                thead=elements[0];
                cols=thead.children.length;
                elements=null; //free memory
                csv={
                    header:'',
                    data:[],
                    footer:'',
                    string:''
                };
            },
            addSlashes=function(data){
                return data.replace(/([\\"])/g,'\\$1');
            },
            render={
                header:function(){
                    if(! (thead.children[0] instanceof window.HTMLTableCellElement))
                    {
                        throw new window.RangeError('At least 1 <tr> element with 1 <td> or <th> is required.');
                    }

                    for(var i=0,children=thead.children,l=children.length,tmp=[];i<l;i++)
                    {
                        tmp[tmp.length]='"'+addSlashes(children[i][prop])+'"';
                    }
                    children=null; //free memory
                    return csv.header=tmp;
                },
                data:function(){

                    if(!tr.length)
                    {
                        return '';
                    }

                    for(var i=0,l=tr.length,tmp=[],tfoot=false;i<l;i++)
                    {
                        if(!tfoot && tr[i].parentNode.tagName=='TFOOT')
                        {
                            tfoot=tr[i];
                            continue;
                        }
                        csv.data[tmp.length]=tmp[tmp.length]=render.row(tr[i]);
                    }

                    if(tfoot)
                    {
                        csv.footer=tmp[tmp.length]=render.row(tfoot);
                    }

                    return tmp.join('\r\n');
                },
                row:function(tr){
                    var td=tr.getElementsByTagName('td');

                    if(!td.length)
                    {
                        td=tr.getElementsByTagName('th');
                    }

                    for(var i=0,tmp=[];i<cols;i++)
                    {
                        tmp[i]=td[i]?'"'+addSlashes(td[i][prop])+'"':'""';
                    }
                    return tmp+'';
                }
            };

        setVars();

        return {
            toString:function(){
                if(csv.string)
                {
                    return csv.string;
                }

                return csv.string = [render.header(),render.data()].join('\r\n');
            },
            valueOf:function(){return this.toString();},
            refresh:function(){
                setVars();
            },
            getHeader:function(){
                return csv.header;
            },
            getFooter:function(){
                return csv.footer;
            },
            getRows:function(){
                return csv.data;
            },
            getRow:function(row){
                return csv.data[row>>0];
            }
        };

    }

})(function(){}.constructor('return this')());

You can check it working in here: http://jsfiddle.net/qw8ponhu/6/

Ismael Miguel
  • 4,185
  • 1
  • 31
  • 42
0

If you aren't using IE you can do it with table2CSV & JQuery

$(document).ready(function() {

  $('table').each(function() {
    var $table = $(this);

    var $button = $("<button type='button'>");
    $button.text("Export to spreadsheet");
    $button.insertAfter($table);

    $button.click(function() {
      var csv = $table.table2CSV({delivery:'value'});
      window.location.href = 'data:text/csv;charset=UTF-8,'
                            + encodeURIComponent(csv);
    });
  });
})
Abercrombieande
  • 679
  • 6
  • 12
  • it doesn't work as I specify on the first 2 lines. see: https://code.google.com/p/table2csv/issues/list – Kokizzu Nov 07 '14 at 13:21
  • I wouldn't consider merged cells as a bug since who is to say it shouldn't be a single value (and you're showing it that way). How is the table getting filled because you might have some options there. – Abercrombieande Nov 07 '14 at 13:50