How to make HTML table cell editable?

HtmlEditorHtml TableCell

Html Problem Overview


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?

Html Solutions


Solution 1 - Html

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 https://stackoverflow.com/questions/1391278/contenteditable-change-events

Solution 2 - Html

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>.

Solution 3 - Html

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.

Solution 4 - Html

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>

Solution 5 - Html

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>

Solution 6 - Html

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

Solution 7 - Html

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/

Solution 8 - Html

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>

Solution 9 - Html

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 :

Solution 10 - Html

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

https://github.com/samuelsantosdev/TableEdit

Solution 11 - Html

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>

Solution 12 - Html

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

Solution 13 - Html

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>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionwqfengView Question on Stackoverflow
Solution 1 - HtmlBrett ZamirView Answer on Stackoverflow
Solution 2 - HtmlvardhanView Answer on Stackoverflow
Solution 3 - HtmlBhavesh GView Answer on Stackoverflow
Solution 4 - HtmlACE ArthurView Answer on Stackoverflow
Solution 5 - Htmlbharti parmarView Answer on Stackoverflow
Solution 6 - HtmlAhmad HalahView Answer on Stackoverflow
Solution 7 - HtmlgsivanovView Answer on Stackoverflow
Solution 8 - Htmlmathmaniac88View Answer on Stackoverflow
Solution 9 - Htmluser3751006View Answer on Stackoverflow
Solution 10 - Htmluser3333866View Answer on Stackoverflow
Solution 11 - HtmlMahmoud SayedView Answer on Stackoverflow
Solution 12 - HtmlYuZhangView Answer on Stackoverflow
Solution 13 - HtmlCarsonView Answer on Stackoverflow