简体   繁体   中英

How do we implement a cancel in plain Javascript?

I have a page and I display data in a table.
In each table I have a column with a checkbox which if is checked the user can modify the specific row via Javascript.
This is done as its td encapsulates either an input or a select and I make these editable for the user.
The user modifies the row and presses save and the changes are saved. So far ok.
My problem is how do I implement a cancel ?
The user could choose many row ie check boxes and modify them but the user could also press cancel. On cancel the original values should be displayed (and the rows become non-editable again).
But how is a cancel operation implemented in Javascript? Do we store data in some global datastructures? Which would be this in Javascript?

Ok, after the addition of informations you provided I suggest you setup the following mecanism:

function getDatas() {
var oXhr;

    //get datas from database:
    oXhr = new XMLHttpRequest();    
    oXhr.onreadystatechange = function() {
    if (oXhr.readyState == 4 && (oXhr.status == 200)) {
        g_oData = (new DOMParser()).parseFromString(oXhr.responseText, "text/xml");
        }
    }

    oXhr.open("POST", "yourphpscriptthatreturnsthexmldatas.php", true);
    oXhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");
    oXhr.send();
}

function populateGrid() {
    //use g_oData to populate your grid, but at first totally clean the body
    var mygrid = document.getElementById("mygridid");
    //mygrid.innerHtml = "<table><tr><td>...</td></tr></table>";

    //use the xml library to parse g_oData and fill up the table:
    var xmlRows = g_oData.getElementsByTagName("TAG");
    var xmlRow;
    iLen = xmlRows.length;
    for (var i=0;i<iLen;i++) {
    xmlRow = xmlRows[i];
    //use xmlRow->textContent to build each cell of your table
    }
}

function revertChange() {
    //on cancel, revert the changes by populating the grid. 
    //it will use the global xml/json object loaded directly from database, to refill everything.
    populateGrid();
}

I did it myself many times to refresh some datas in a page. That's basically what you're doing except that you're not requesting anything to the database, you just refill the fields.

You can use the HTML5 data- attributes to implement a revert function. This way, each <input> would hold it's original value in case a revert button would be used.

Here's how it'd look:

<table>
  <tr>
     <td><input type='text' value='change me' data-original='change me' /></td>
     <td><input type='text' value='change me2' data-original='change me2' /></td>
     <td><input type='button' value='revert' onclick='revert(this)'/></td>
  </tr>
<table>

And the code that reverts:

function revert(btn) {
    var parentTr = btn.parentNode.parentNode;
    var inputs = parentTr.getElementsByTagName('input');
    for(var i = 0; i < inputs.length; i++) {
        if (inputs[i].type == 'text') {
            inputs[i].value = inputs[i].getAttribute('data-original');
        }
    }
}

The data-original attribute could be generated:


As a side solution, you could store the original values in a map object. Here's the (3) demo for this (notice I added the id for each input , so it can be used as key to the map ).

Keep in mind, though, neither solutions (2) or (3) require changing in server side code (the 3 assuming your inputs have ids). And (2) feels clearer.

About the defaultValue attribute:
The defaultValue attribute can be a solution only if the value to be reverted never changes and if the fields involved are text input s.

Firstly, changing the "default value" is rather awkward and may break something else aling the page (one would expect the browsers make the defaultValue attribute read-only, but that does not seem to be the case). Secondly, you would be limited to input s of the text type.

Still, if none of that is a problem, the code above can be quickly adapted to use them instead of data- attributes.

Here is a pretty simple way:

Don't replace the cell content with the form element. Keep the value (the text) in a span element and hide it when you show the form element. Then you don't have to do anything on cancel. Just show the span again and hide or remove the form element. Only update the span when the user wants to save the value.

Here is an example. The showing and hiding is all done with CSS.

<tr>
    <td>
        <span>value</span>
        <input type='text' value='' />
    </td>
    <td>
       <button class="save">Save</button>
       <button class="revert">Revert</button>
    </td>
</tr>

JS:

var rows = document.querySelectorAll('table tr');

for(var i = 0, l = rows.length; i < l; i++) {
    rows[i].addEventListener('click', function(event) {
        // all value display elements in the row
        var spans = this.querySelectorAll('span');
        // all form elements in the row
        var inputs = this.querySelectorAll('input');

        // handle click on save button
        if (event.target.className === 'save') {
            [].forEach.call(inputs, function(input, i) {
                spans[i].innerHTML = input.value;
            });
            this.className = '';
        }
        // handle click on revert button
        else if (event.target.className === 'revert') {
             // not much to do
             this.className = '';
        }
        else {
            // update form element values
            [].forEach.call(inputs, function(input, i) {
                input.value = spans[i].innerHTML;
            });
             this.className = 'edit';
        }
    });
}

DEMO

You can just access the original value attribute of the input to get the defaultValue . Sample implementation:

$("table").on("dblclick", "td", function(e) {
    var val = $(this).html();
    $(this).empty().append($("<form/>").append(
        $("<input/>", {type:"text"}).attr("value", val),
        //                           ^^^^
        // set the *attribute*, as if it was present in the parsed HTML
        $("<button/>", {type:"reset"}).text("Reset"),
        $("<button/>", {type:"button", class:"cancel"}).text("Cancel"),
        $("<button/>", {type:"submit"}).text("Submit")
    ));
}).on("submit", "form", function(e) {
    var val = $(this).find("input:text").val();
    //                                   ^^^^^
    // which is equivalent to .prop("value")

    /* then do something with val, e.g. send it to server via ajax */
    $(this).parent().html(val);
    e.preventDefault();
}).on("click", "button.cancel", function(e) {
    var $form = $(this).parent(),
        $input = $form.find("input:text"),
        oldval = $input.attr("value");
    //                  ^^^^^^^^^^^^^
    // or .prop("defaultValue"), but not .val()!
    if (oldval == $input.val() || confirm("Do you really want to discard your changes?"))
        $(this).parent().html(oldval);
    e.preventDefault();
});

( Demo at jsfiddle.net )

A maybe more simple solution might be to use the dblclick-handler that creates the form as a closure and just store the original html in a local variable there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM