How do I prevent/manage my input event handler getting unbound when data changes?

How do I prevent/manage my input event handler getting unbound when data changes?

JLavarnwayJLavarnway Posts: 3Questions: 1Answers: 0
edited November 2022 in Free community support

I am currently attempting to create a table in which certain cells are filled with input fields containing that cell's data. When a user triggers an input event against this field, the data from the row's inputs should be collected and placed as the row's data.

Currently, this only works for the first time the user performs an input. Once the data of the row is set, the input event listener is unbound. Attempting to rebind this event listener fails.

I am currently unable to obtain the Editor library due to red tape.

Table as rendered in HTML.


<table id="myTable"> <thead> <tr> <th scope="col"></th> <!--Empty for control buttons. --> <th scope="col">Item Description.</th> <th scope="col">Item Count</th> <!--Item Id - not editable or visible--> </tr> </thead> <tbody> <tr> <td> <button class="remove-item-button">Remove Item</button> </td> <td>Motor Oil</td> <td>5</td> <td>9999</td> </tr> </tbody> </table>

JavaScript to turn table into DataTable:

$(document).ready(function(){
    $("#myTable").DataTable({
        columnDefs: [

            //Item Description
            {
                targets: 1,
                className: "item-description-cell",
                render: function(data, type, row, meta) {
                    var result = $(document.createElement('input'))
                        .on('input',function (e) {updateDescriptionData(e) })
                        .attr("value",data)
                        .prop('outerHTML');

                    return result;
                    }
            },

            //Item Count
        {
                targets: 2,
                className: "item-count-cell",
                render: function(data, type, row, meta) {
                    var result = $(document.createElement('input'))
                        .on('input',function (e) {updateCountData(e) })
                        .attr("value",data)
                        .prop('outerHTML');

                    return result;
                    }
            },
                
                //Item Id - Invisible.
            {
                    targets: 3,
                    visible: false,
                    searchable: false
            }
        ]
    })
})

Functions to retrieve and set data:

function updateDescriptionData(e) {
    let $table = $(e.currentTarget).closest("table").DataTable();
    let $row = $table.row( $(e.currentTarget).closest("tr") );

    //1. Get data for the current row.
    let newData = $row.data();

    //2. Update the element at index 1 - the item's description, with the value in the input field.
    newData[1] = $(e.currentTarget).val();

    //3. Put data back into the table.
    //    For some reason, doing so destroys the 'input' event listener on the input element.
    $row.data(newData);


    //4. Attempting to re-add the event listener does nothing.
    $(e.currentTarget).on('input', function(e) { updateDescriptionData(e) });

}

//....Similar code for updateCountData cut for brevity...

So far I have attempted to re-up the event handler after setting the data, as you see above, and I have tried attaching the input event handler as part of the render option's result. Both have still resulted in the input handler disappearing. How do I prevent this handler from vanishing?

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,327Questions: 26Answers: 4,949

    The columns.render function can run multiple times which will keep adding event handlers for the inputs each time it runs. Best practice is to use delegated events like this example. They only need created once.

    Kevin

  • JLavarnwayJLavarnway Posts: 3Questions: 1Answers: 0
    edited November 2022

    I changed my code to remove the .on() function from render, and instead place it after the DataTable object was completed. No dice.

    $(document).ready(function(){  
        $(document).ready(function(){
        let $table = $("#myTable").DataTable({
            columnDefs: [
     
                //Item Description
                {
                    targets: 1,
                    className: "item-description-cell",
                    render: function(data, type, row, meta) {
                        var result = $(document.createElement('input'))
                            .prop({class: "item-description-input"})
                            .attr("value",data)
                            .prop('outerHTML');
     
                        return result;
                        }
                },
     
                //Item Count
            {
                    targets: 2,
                    className: "item-count-cell",
                    render: function(data, type, row, meta) {
                        var result = $(document.createElement('input'))
                            .prop({class: "item-count-input"})
                            .attr("value",data)
                            .prop('outerHTML');
     
                        return result;
                        }
                },
                     
                    //Item Id - Invisible.
                {
                        targets: 3,
                        visible: false,
                        searchable: false
                }
            ]
        })
    })
    
        $table.find(".item-description-input").on('input', function (e) { updateDescriptionData(e) })
    
        $table.find(".item-count-input").on('input', function (e) { updateCountData(e) })
    
    })
    

    The input event handler on my fields still vanishes after $row.data(newData); is executed.

  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Answer ✓

    Try:

    $("#myTable").on('input', '.item-description-input', function () {
      ...
    });
    

    That uses a delegated handler, like Kevin suggested.

    Allan

  • kthorngrenkthorngren Posts: 21,327Questions: 26Answers: 4,949

    Not sure what $table is but it does't seem like you set up the delegated events correctly. Maybe try something like this:

    ``js
    $('#example tbody").on('input', '.item-description-input', function (.....
    ```

    See the jQuery delegated events tutorial for more info.

    If you still need help please provide a simple test case we can take a look at to help debug.
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • JLavarnwayJLavarnway Posts: 3Questions: 1Answers: 0

    Thanks Allan. That seems to have...mostly fixed it. The input event handler is no longer vanishing, and data is getting set properly.

    However, the input element is now losing focus after the user types a single character. Any ideas on how to prevent that?

  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin

    If updateDescriptionData is triggering a redraw of the table, then that would cause what you are seeing. The only way to address that would be to wait until the full data has been entered (perhaps a delay of 1000mS after input, or wait for a return key press).

    Allan

Sign In or Register to comment.