How to make HTML table cell editable?
Asked Answered
F

14

133

I'd like to make some cells of html table editable, simply double click a cell, input some text and the changes can be sent to server. I don't want to use some toolkits like dojo data grid. Because it provides some other features. Would you provide me some code snippet or advices on how to implement it?

Fergus answered 16/5, 2011 at 3:5 Comment(0)
S
151

You can use the contenteditable attribute on the cells, rows, or table in question.

Updated for IE8 compatibility

<table>
<tr><td><div contenteditable>I'm editable</div></td><td><div contenteditable>I'm also editable</div></td></tr>
<tr><td>I'm not editable</td></tr>
</table>

Just note that if you make the table editable, in Mozilla at least, you can delete rows, etc.

You'd also need to check whether your target audience's browsers supported this attribute.

As far as listening for the changes (so you can send to the server), see contenteditable change events

Selfrestraint answered 16/5, 2011 at 3:12 Comment(9)
Thank you. It seems contenteditable is supported in HTML5. I'm looking for a solution which works in html4 too.Fergus
Although it is finally codified in the standard with HTML5, it was already well supported in most older browsers (with exception of only partial support in FF3): caniuse.com/contenteditable (though not in mobile devices)Selfrestraint
Please explain to me why there must be a DIV in the cell to make it work for IE. Using <td contenteditable=true> doesn't make the cell editable in IE. I thought contenteditable was for any HTML element?Zabaglione
That's just the way it is with IE: "Though the TABLE, COL, COLGROUP, TBODY, TD, TFOOT, TH, THEAD, and TR elements cannot be set as content editable directly, a content editable SPAN, or DIV element can be placed inside the individual table cells (TD and TH elements)." msdn.microsoft.com/en-us/library/ie/ms533690%28v=vs.85%29.aspx .Selfrestraint
It appears you are right that it should be for any HTML element. For the HTMLElement interface defined at html.spec.whatwg.org/multipage/… -> implements ElementContentEditable -> defined at html.spec.whatwg.org/multipage/… -> references "editing host" at dvcs.w3.org/hg/editing/raw-file/tip/editing.html#editing-host -- none of these seem to allow for restricting which elements support contenteditable. Welcome to the wonderful world of browser bugs :)Selfrestraint
@BrettZamir my table implement itself depending on the data (the more data, the more <td>). How could I set all <td> being editable by default?Troublemaker
If you need IE8 compatibility, you just have to add the contenteditable div whenever you create a new <td>. Otherwise, as mentioned in the post, you can add the contenteditable on the cells, rows, or table.Selfrestraint
How can I actually store the data with html that will be entered in the table cell. I don't want to send the data (as I have no server). I want to overwrite the original html file with the input data. Is it possible?Clarethaclaretta
@Clarethaclaretta : No, you can't do that in a normal website because normal websites don't have privileges to overwrite files on your desktop for security reasons. If it is privileged HTML in some way, such as a browser add-on may allow, you may be able to serialize the HTML and save it to disk. But this is off topic for this thread.Selfrestraint
C
88

HTML5 supports contenteditable,

<table border="3">
<thead>
<tr>Heading 1</tr>
<tr>Heading 2</tr>
</thead>
<tbody>
<tr>
<td contenteditable='true'></td>
<td contenteditable='true'></td>
</tr>
<tr>
<td contenteditable='true'></td>
<td contenteditable='true'></td>
</tr>
</tbody>
</table>

3rd party edit

To quote the mdn entry on contenteditable

The attribute must take one of the following values:

  • true or the empty string, which indicates that the element must be editable;

  • false, which indicates that the element must not be editable.

If this attribute is not set, its default value is inherited from its parent element.

This attribute is an enumerated one and not a Boolean one. This means that the explicit usage of one of the values true, false or the empty string is mandatory and that a shorthand ... is not allowed.

// wrong not allowed
<label contenteditable>Example Label</label> 

// correct usage
<label contenteditable="true">Example Label</label>.
Chromatology answered 18/3, 2014 at 4:25 Comment(3)
Weird. Usually the attribute value isn't true, it's whatever the name is. For example, <td contenteditable='contenteditable'></td>.Bern
Possible states of contenteditable: contenteditable**="" or **contenteditable**="true" Indicates that the element is editable. **contenteditable**="false" Indicates that the element is not editable. **contenteditable**="inherit" Indicates that the element is editable if its immediate parent element is editable. This is the default value. When you add **contenteditable to an element, the browser will make that element editable. In addition, any children of that element will also become editable unless the child elements are explicitly **contenteditable**="false".Chromatology
I know that, I just thought it was peculiar because most other attributes don't have that syntax.Bern
C
26

I have three approaches, Here you can use both <input> or <textarea> as per your requirements.

1. Use Input in <td>.

Using <input> element in all <td>s,

<tr><td><input type="text"></td>....</tr>

Also, you might want to resize the input to the size of its td. ex.,

input { width:100%; height:100%; }

You can additionally change the colour of the border of the input box when it is not being edited.

2. Use contenteditable='true' attribute. (HTML5)

However, if you want to use contenteditable='true', you might also want to save the appropriate values to the database. You can achieve this with ajax.

You can attach keyhandlers keyup, keydown, keypress etc to the <td>. Also, it is good to use some delay() with those events when user continuously types, the ajax event won't fire with every key user press. for example,

$('table td').keyup(function() {
  clearTimeout($.data(this, 'timer'));
  var wait = setTimeout(saveData, 500); // delay after user types
  $(this).data('timer', wait);
});
function saveData() {
  // ... ajax ...
}

3. Append <input> to <td> when it is clicked.

Add the input element in td when the <td> is clicked, replace its value according to the td's value. When the input is blurred, change the `td's value with the input's value. All this with javascript.

Castrate answered 19/7, 2015 at 12:17 Comment(3)
Unfortunately you missed the question part "How to make HTML table cell editable?" especially in example 2. The user asked how to achieve this on doubleclick. Can you kindly implement the missing part?Nanice
@BhaveshGangani i have some issue with contenteditable=true can you help me out in this please?Otic
Sure I can try. Do you have a js fiddle for that?Castrate
H
21

This is a runnable example.

$(function(){
  $("td").click(function(event){
    if($(this).children("input").length > 0)
          return false;

    var tdObj = $(this);
    var preText = tdObj.html();
    var inputObj = $("<input type='text' />");
    tdObj.html("");

    inputObj.width(tdObj.width())
            .height(tdObj.height())
            .css({border:"0px",fontSize:"17px"})
            .val(preText)
            .appendTo(tdObj)
            .trigger("focus")
            .trigger("select");

    inputObj.keyup(function(event){
      if(13 == event.which) { // press ENTER-key
        var text = $(this).val();
        tdObj.html(text);
      }
      else if(27 == event.which) {  // press ESC-key
        tdObj.html(preText);
      }
    });

    inputObj.click(function(){
      return false;
    });
  });
});
<html>
    <head>
        <!-- jQuery source -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    </head>
    <body>
        <table align="center">
            <tr> <td>id</td> <td>name</td> </tr>
            <tr> <td>001</td> <td>dog</td> </tr>
            <tr> <td>002</td> <td>cat</td> </tr>
            <tr> <td>003</td> <td>pig</td> </tr>
        </table>
    </body>
</html>
Hexahedron answered 8/10, 2016 at 14:53 Comment(0)
L
9

I am using this for editable field

<table class="table table-bordered table-responsive-md table-striped text-center">
  <thead>
    <tr>
      <th class="text-center">Citation</th>
      <th class="text-center">Security</th>
      <th class="text-center">Implementation</th>
      <th class="text-center">Description</th>
      <th class="text-center">Solution</th>
      <th class="text-center">Remove</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="pt-3-half" contenteditable="false">Aurelia Vega</td>
      <td class="pt-3-half" contenteditable="false">30</td>
      <td class="pt-3-half" contenteditable="false">Deepends</td>
      <td class="pt-3-half" contenteditable="true"><input type="text" name="add1" value="spain" class="border-none"></td>
      <td class="pt-3-half" contenteditable="true"><input type="text" name="add1" value="marid" class="border-none"></td>
      <td>
        <span class="table-remove"><button type="button"
                              class="btn btn-danger btn-rounded btn-sm my-0">Remove</button></span>
      </td>
    </tr>
  </tbody>
</table>
Lookthrough answered 17/3, 2020 at 7:22 Comment(0)
W
6

You can use x-editable https://vitalets.github.io/x-editable/ its awesome library from bootstrap

Weakminded answered 20/12, 2016 at 13:43 Comment(0)
W
5

Just insert <input> element in <td> dynamically, on cell click. Only simple HTML and Javascript. No need for contentEditable , jquery, HTML5

https://Jsfiddle.net/gsivanov/38tLqobw/2/

Wheaton answered 19/9, 2017 at 14:43 Comment(0)
L
4

Try this code.

$(function () {
 $("td").dblclick(function () {
    var OriginalContent = $(this).text();

    $(this).addClass("cellEditing");
    $(this).html("<input type="text" value="&quot; + OriginalContent + &quot;" />");
    $(this).children().first().focus();

    $(this).children().first().keypress(function (e) {
        if (e.which == 13) {
            var newContent = $(this).val();
            $(this).parent().text(newContent);
            $(this).parent().removeClass("cellEditing");
        }
    });

 $(this).children().first().blur(function(){
    $(this).parent().text(OriginalContent);
    $(this).parent().removeClass("cellEditing");
 });
 });
});

You can also visit this link for more details :

Leitmotiv answered 18/6, 2014 at 5:22 Comment(1)
To avoid problems in IE with $(this).children().first().focus(); - https://mcmap.net/q/169065/-jquery-focus-sometimes-not-working-in-ie8Hillier
S
4

This is the essential point although you don't need to make the code this messy. Instead you could just iterate through all the <td> and add the <input> with the attributes and finally put in the values.

function edit(el) {
  el.childNodes[0].removeAttribute("disabled");
  el.childNodes[0].focus();
  window.getSelection().removeAllRanges();
}
function disable(el) {
  el.setAttribute("disabled","");
}
<table border>
<tr>
<td ondblclick="edit(this)"><input value="cell1" disabled onblur="disable(this)"></td>
<td ondblclick="edit(this)"><input value="cell2" disabled onblur="disable(this)"></td>
<td ondblclick="edit(this)"><input value="cell3" disabled onblur="disable(this)"></td>
<td ondblclick="edit(this)"><input value="so forth..." disabled onblur="disable(this)">
</td>
</tr>
</table>
Sweaty answered 17/5, 2018 at 2:3 Comment(0)
H
3

If you use Jquery, this plugin help you is simple, but is good

https://github.com/samuelsantosdev/TableEdit

Humming answered 20/2, 2014 at 17:18 Comment(1)
That looks like an interesting plugin. The documentation for how to use it can be found in the index.html file. Please take a look at meta.stackexchange.com/questions/8231/… to see why including more information than just a link makes for a better answer.Oriental
S
2

FOR SET A CELL EDITABLE YOU CAN USE THIS KEYWORD "contenteditable" THIS IS AN EXAMPLE

tr td {
  border: solid 2px black
}
<table>
  <tr>
    <td contenteditable>CELL EDITABLE</td><!--CELL EDITABLE-->
    <td contenteditable>CELL EDITABLE</td><!--CELL EDITABLE-->
  </tr>
  <tr>
    <td>CELL NOT EDITABLE</td> 
    <td>CELL NOT EDITABLE</td>
  </tr>
</table>
Swampland answered 11/4, 2023 at 14:52 Comment(0)
T
0

this is actually so straight forward, this is my HTML, jQuery sample.. and it works like a charm, I build all the code using an online json data sample. cheers

<< HTML >>

<table id="myTable"></table>

<< jQuery >>

<script>
        var url = 'http://jsonplaceholder.typicode.com/posts';
        var currentEditedIndex = -1;
        $(document).ready(function () {
            $.getJSON(url,
            function (json) {
                var tr;
                tr = $('<tr/>');
                tr.append("<td>ID</td>");
                tr.append("<td>userId</td>");
                tr.append("<td>title</td>");
                tr.append("<td>body</td>");
                tr.append("<td>edit</td>");
                $('#myTable').append(tr);

                for (var i = 0; i < json.length; i++) {
                    tr = $('<tr/>');
                    tr.append("<td>" + json[i].id + "</td>");
                    tr.append("<td>" + json[i].userId + "</td>");
                    tr.append("<td>" + json[i].title + "</td>");
                    tr.append("<td>" + json[i].body + "</td>");
                    tr.append("<td><input type='button' value='edit' id='edit' onclick='myfunc(" + i + ")' /></td>");
                    $('#myTable').append(tr);
                }
            });


        });


        function myfunc(rowindex) {

            rowindex++;
            console.log(currentEditedIndex)
            if (currentEditedIndex != -1) {  //not first time to click
                cancelClick(rowindex)
            }
            else {
                cancelClick(currentEditedIndex)
            }

            currentEditedIndex = rowindex; //update the global variable to current edit location

            //get cells values
            var cell1 = ($("#myTable tr:eq(" + (rowindex) + ") td:eq(0)").text());
            var cell2 = ($("#myTable tr:eq(" + (rowindex) + ") td:eq(1)").text());
            var cell3 = ($("#myTable tr:eq(" + (rowindex) + ") td:eq(2)").text());
            var cell4 = ($("#myTable tr:eq(" + (rowindex) + ") td:eq(3)").text());

            //remove text from previous click


            //add a cancel button
            $("#myTable tr:eq(" + (rowindex) + ") td:eq(4)").append(" <input type='button' onclick='cancelClick("+rowindex+")' id='cancelBtn' value='Cancel'  />");
            $("#myTable tr:eq(" + (rowindex) + ") td:eq(4)").css("width", "200");

            //make it a text box
            $("#myTable tr:eq(" + (rowindex) + ") td:eq(0)").html(" <input type='text' id='mycustomid' value='" + cell1 + "' style='width:30px' />");
            $("#myTable tr:eq(" + (rowindex) + ") td:eq(1)").html(" <input type='text' id='mycustomuserId' value='" + cell2 + "' style='width:30px' />");
            $("#myTable tr:eq(" + (rowindex) + ") td:eq(2)").html(" <input type='text' id='mycustomtitle' value='" + cell3 + "' style='width:130px' />");
            $("#myTable tr:eq(" + (rowindex) + ") td:eq(3)").html(" <input type='text' id='mycustomedit' value='" + cell4 + "' style='width:400px' />");

        }

        //on cancel, remove the controls and remove the cancel btn
        function cancelClick(indx)
        {

            //console.log('edit is at row>> rowindex:' + currentEditedIndex);
            indx = currentEditedIndex;

            var cell1 = ($("#myTable #mycustomid").val());
            var cell2 = ($("#myTable #mycustomuserId").val());
            var cell3 = ($("#myTable #mycustomtitle").val());
            var cell4 = ($("#myTable #mycustomedit").val()); 

            $("#myTable tr:eq(" + (indx) + ") td:eq(0)").html(cell1);
            $("#myTable tr:eq(" + (indx) + ") td:eq(1)").html(cell2);
            $("#myTable tr:eq(" + (indx) + ") td:eq(2)").html(cell3);
            $("#myTable tr:eq(" + (indx) + ") td:eq(3)").html(cell4);
            $("#myTable tr:eq(" + (indx) + ") td:eq(4)").find('#cancelBtn').remove();
        }



    </script>
Transmutation answered 10/7, 2016 at 6:58 Comment(0)
P
0

Add <input> to <td> when it is clicked. Change <input> to <span> when it is blurred.

Prune answered 16/9, 2020 at 9:10 Comment(0)
R
-1

If your table has many functions (sorting, exporting, updating, editing, etc.),

I would recommend the bootstrap table.

wenzhixin/bootstrap-table

About editing practices:

Listen to events: click-cell.bs.table then adds the attribute contenteditable to the td as soon as it is clicked

You may not want to allow editing in every column, so I added the data-editable attribute myself with JS to determine this.

Demo

column of Title allows you to edit

<!DOCTYPE html>
<html>
<head>
  <!-- jquery -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous"></script>

  <!-- bootstrap -->
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script>

  <!-- bootstrap-table-->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.18.3/bootstrap-table.min.css" integrity="sha512-5RNDl2gYvm6wpoVAU4J2+cMGZQeE2o4/AksK/bi355p/C31aRibC93EYxXczXq3ja2PJj60uifzcocu2Ca2FBg==" crossorigin="anonymous" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.18.3/bootstrap-table.min.js" integrity="sha512-Wm00XTqNHcGqQgiDlZVpK4QIhO2MmMJfzNJfh8wwbBC9BR0FtdJwPqDhEYy8jCfKEhWWZe/LDB6FwY7YE9QhMg==" crossorigin="anonymous"></script>

  <!--bootstrap-table-lanuage-->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.18.3/bootstrap-table-locale-all.min.js" integrity="sha512-1PCRWIvrSQaZjCRWaa0GHWKr1jQA8u79VnIvkAme6BKeoNWe5N89peawTXdVp+kukb8rzNsEY89ocMJqVivdSA==" crossorigin="anonymous"></script>
  <!--bootstrap-table-export-->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.18.3/extensions/export/bootstrap-table-export.min.js" integrity="sha512-cAMZL39BuY4jWHUkLWRS+TlHzd/riowdz6RNNVI6CdKRQw1p1rDn8n34lu6pricfL0i8YXeWQIDF5Xa/HBVLRg==" crossorigin="anonymous"></script>

  <!-- screenshots -->
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/es6-promise.auto.min.js" integrity="sha256-Xxrdry6fWSKu1j2ALyuK/gHFQ+2Bp76ZrR6f3QXI47Y=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js" integrity="sha256-Ax1aqtvxWBY0xWND+tPZVva/VQZy9t1Ce17ZJO+NTRc=" crossorigin="anonymous"></script>


  <!-- tableexport.jquery.plugin If you want to export, then you must add it. -->
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/tableExport.min.js" integrity="sha256-Dsris8trQzzQXIM6PgMzSugaNyUacxaR9o2VrJalh6Y=" crossorigin="anonymous"></script>

  <!-- font-awesome -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/js/all.min.js" integrity="sha512-RXf+QSDCUQs5uwRKaDoXt55jygZZm2V++WUZduaU/Ui/9EGp3f/2KZVahFZBKGH0s774sd3HmrhUy+SgOFQLVQ==" crossorigin="anonymous"></script>

  <style>
    html {
      font-family: sans-serif;
      line-height: 1.15;
      -webkit-text-size-adjust: 100%;
      -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
    }

    h1, h2, h3, h4, h5, h6 {
      margin-top: 0;
      margin-bottom: 0.5rem;
      color: #004a88;
      text-align: center;
    }

    .table-blue {
      font-family: Arial, Helvetica, sans-serif;
      border-collapse: collapse;
      width: 100%;
    }

    .table-blue td, .table-blue th {
      border: 1px solid #ddd;
      padding: 8px;
    }

    .table-blue tr:hover {background-color: #ddd;}

    .table-blue th {
      background-color: #004a88;
      font-size: larger;
      font-weight: bold;
      padding-top: 5px;
      padding-bottom: 5px;
      text-align: left;
      color: white;
    }

    /* https://stackoverflow.com/a/63412885 */
    thead, tbody tr {
      display: table;
      width: 100%;
      table-layout: fixed;
    }
    tbody {
      display: block;
      overflow-y: auto;
      table-layout: fixed;
      max-height: 512px;
    }

    td {
      word-break: break-all;
    }

  </style>
</head>

<body>
<!-- Table-options:
- https://bootstrap-table.com/docs/api/table-options/
- https://bootstrap-table.com/docs/extensions/export/
-->
<table id="myTable" class="table table-striped table-blue"
       data-toggle="table"
       data-search="true"
       data-search-highlight="true"
       data-show-refresh="true"
       data-show-toggle="true"
       data-show-columns="true"
       data-show-export="true"
       data-minimum-count-columns="2"
       data-show-pagination-switch="true"
       data-pagination="true"
       data-id-field="id"
       data-page-list="[10, 25, 50, 100, ALL]"
       data-show-footer="false"
       data-side-pagination="client"
       data-export-types='["csv", "json", "excel", "doc", "sql", "png"]'
       data-editable = '[false, true, false, false]'
       data-export-options='{
        "fileName": "products"
        }'
       data-url="https://jsonplaceholder.typicode.com/photos">
  <thead>
  <tr>
    <th data-sortable="true" data-field="id">Id</th>
    <th data-sortable="true" data-field="title">Title</th>
    <th data-sortable="true" data-field="url">URL</th>
    <th data-sortable="true" data-formatter="imageFormatter" data-field="thumbnailUrl">Thumbnail URL</th>
  </tr>
  </thead>
</table>
</body>

<script>
  const TABLE_ID = "myTable";
  const TABLE = document.getElementById(TABLE_ID)

  window.onload = () => {
    const table = $(`#${TABLE_ID}`)

    function imageFormatter(value, row) {
      return `<img src="${value}"  style="width:60px;height:60px" loading="lazy"/>`;
    }

    function saveData(tdData) {
      // ... ajax ...
      console.log("save")
    }

    const infoEditable = JSON.parse(TABLE.getAttribute("data-editable"))
    if (infoEditable === null) {
      return
    }
    table.on('click-cell.bs.table', function (event, field, value, row, td) {
      td = td[0]
      if (td.getAttribute("contentEditable")) {
        return
      }
      const index = Array.prototype.indexOf.call(td.parentNode.children, td)
      if (infoEditable[index]) {
        td.contentEditable = "true"
      }

      td.addEventListener("keyup", (event) => {
        clearTimeout($.data(this, 'timer'));
        const wait = setTimeout(saveData, 1000); // delay after user types
        $(this).data('timer', wait);
      })
    })
  }
</script>
</html>
Reaves answered 20/4, 2021 at 10:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.