129

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?

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
wqfeng
  • 1,461
  • 3
  • 11
  • 11

14 Answers14

148

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

Community
  • 1
  • 1
Brett Zamir
  • 14,034
  • 6
  • 54
  • 77
  • 1
    Thank you. It seems contenteditable is supported in HTML5. I'm looking for a solution which works in html4 too. – wqfeng May 16 '11 at 04:54
  • 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): http://caniuse.com/contenteditable (though not in mobile devices) – Brett Zamir May 16 '11 at 05:00
  • 1
    Please explain to me why there must be a DIV in the cell to make it work for IE. Using `` doesn't make the cell editable in IE. I thought contenteditable was for *any* HTML element? – Rafael Feb 25 '15 at 00:59
  • 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)." https://msdn.microsoft.com/en-us/library/ie/ms533690%28v=vs.85%29.aspx . – Brett Zamir Feb 25 '15 at 04:50
  • It appears you are right that it should be for any HTML element. For the `HTMLElement` interface defined at https://html.spec.whatwg.org/multipage/dom.html#elements-in-the-dom:element -> implements `ElementContentEditable` -> defined at https://html.spec.whatwg.org/multipage/interaction.html#contenteditable:attr-contenteditable -> references "editing host" at https://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 :) – Brett Zamir Feb 25 '15 at 04:52
  • @BrettZamir my table implement itself depending on the data (the more data, the more ). How could I set all being editable by default? – JinSnow Apr 02 '15 at 09:13
  • 1
    If you need IE8 compatibility, you just have to add the `contenteditable` div whenever you create a new ``. Otherwise, as mentioned in the post, you can add the `contenteditable` on the cells, rows, or table. – Brett Zamir Apr 02 '15 at 14:00
  • 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? – tt_pre Feb 22 '21 at 09:50
  • @tt_pre : 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. – Brett Zamir Feb 22 '21 at 13:15
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>.
surfmuggle
  • 5,527
  • 7
  • 48
  • 77
vardhan
  • 1,377
  • 11
  • 14
  • Weird. Usually the attribute value isn't `true`, it's whatever the name is. For example, ``. – trysis Jul 07 '14 at 18:47
  • 1
    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". – vardhan Nov 20 '14 at 11:49
  • 1
    I know that, I just thought it was peculiar because most other attributes don't have that syntax. – trysis Nov 20 '14 at 21:44
24

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.

Bhavesh G
  • 3,000
  • 4
  • 39
  • 66
  • 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? – Robert Sep 19 '17 at 06:36
  • @BhaveshGangani i have some issue with `contenteditable=true` can you help me out in this please? –  Jan 11 '19 at 10:01
  • 1
    Sure I can try. Do you have a js fiddle for that? – Bhavesh G Jan 11 '19 at 19:29
20

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>
user883992158
  • 325
  • 3
  • 17
ACE Arthur
  • 201
  • 2
  • 5
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>
bharti parmar
  • 394
  • 3
  • 12
6

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

Ahmad Halah
  • 81
  • 1
  • 5
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/

gsivanov
  • 123
  • 2
  • 3
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 :

Engineer
  • 1,436
  • 3
  • 18
  • 33
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>
mathmaniac88
  • 630
  • 7
  • 22
3

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

https://github.com/samuelsantosdev/TableEdit

  • 3
    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 http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers#answer-8259 to see why including more information than just a link makes for a better answer. – Jason Aller Feb 20 '14 at 17:40
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>
Mahmoud Sayed
  • 151
  • 2
  • 10
0

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

0

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>
-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/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/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/es6-promise@4.2.8/dist/es6-promise.auto.min.js" integrity="sha256-Xxrdry6fWSKu1j2ALyuK/gHFQ+2Bp76ZrR6f3QXI47Y=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.7/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/tableexport.jquery.plugin@1.10.22/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>
Carson
  • 6,105
  • 2
  • 37
  • 45