Craig
Craig

Reputation: 18694

Getting and Setting a data attribute in HTML

I have an edit box, defined like this:

<input class="change-handled form-control" type-id="@sub.CategoryTypeId" sub-category-id="@sub.SubCategoryId" data-id="@sub.CategoryBudgetId" style="text-align: right; width: 100%" type="number" value="@(sub.BudgetAmount.HasValue ? sub.BudgetAmount.ToString() : "")" />

In Javascript, I get the data-id value successfully like this:

var dataId = $(this).attr('data-id');

I now need to set it to a different value. I am trying:

   $(this).setAttribute("data-id", 5);

But it seems the data-id never gets set to the value I pass. How can I set the data-id value of my editbox?

Full code of the function being used. (Note, no error checking yet):

$('body').on('change', 'input.change-handled', UpdateTotals);

function UpdateTotals() {
    var dataId = $(this).attr('data-id');
    var categoryId = $(this).attr('sub-category-id');
    var value = $(this).val();

    var totalExp = 0;
    var totalInc = 0;

    var $changeInputs = $('input.change-handled');

    $changeInputs.each(function (idx, el) {
        if ($(el).attr('type-id') == 2) {
            totalInc += Number($(el).val());
        }
        if ($(el).attr('type-id') == 1) {
            totalExp += Number($(el).val());
        }
    });

     $(this).val(numberWithCommas(value));

    $('#budgettedExpenseValue').text(numberWithCommas(totalExp));
    $('#budgettedIncomeValue').text(numberWithCommas(totalInc));
    $('#budgettedAvailableValue').text(numberWithCommas(totalInc - totalExp));

    $.ajax({
        url: '@Url.Action("SaveBudgetValue", "Budget")',
        type: "POST",
        contentType: "application/json",
        data: JSON.stringify({ budgetCategoryId: dataId, catgoryId: categoryId, month: 4, year: 2015, value: value }),
        cache: false,
        async: true,
        success: function (result) {

            if (result.Success == 'true') {
                $(this).attr("data-id", result.Id);

                alert("Saved! " + result.Id.toString());
            } else {
                alert("Failed");
            }
        },
        error: function () {
            alert("Oh no...");
        }

    });

The code, after an edit box of the class type is edited, sums up all the income boxes (Decorated with a type-id = 1), and updates a field, and all the expense boxes (type-id = 2) and updates a separate field.

It then saves the data with a json call to my controller. If it's a new entry, data-id would have been NULL. The save method returns the primary key of the value saved. That value is displayed in my alert, and is supposed to be assigned to the edit boxe's data-id. But - isn't.

Upvotes: 0

Views: 1176

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074365

Re your update

The problem is that in the ajax success callback, this doesn't refer to the element anymore.

Two ways to fix that, and a third way that will be available in ES6:

  1. Assign this, or more usefully $(this), to a variable that you use in the success handler (and elsewhere, no need to constantly call $() repeatedly on the same element):

    function UpdateTotals() {
        var input = $(this); // <========== Save it here
    
        // ...
    
        $.ajax({
            // ...
            success: function (result) {
                // ...
                if (result.Success == 'true') {
                    input.attr("data-id", result.Id); // <========= Use it here
    
                    alert("Saved! " + result.Id.toString());
                } else {
                    alert("Failed");
                }
            }
        });
    
        // ...
    }
    
  2. Use Function#bind (an ES5 feature, but it can be shimmed for really old browsers) to make this within the callback the same as this outside it:

    function UpdateTotals() {
    
        // ...
    
        $.ajax({
            // ...
            success: function (result) {
                // ...
                if (result.Success == 'true') {
                    $(this).attr("data-id", result.Id);
    
                    alert("Saved! " + result.Id.toString());
                } else {
                    alert("Failed");
                }
            }.bind(this) // <=========== Note
        });
    
        // ...
    }
    
  3. In ES6, we'll have arrow functions, which unlike normal functions inherit the this of the context in which they're created. So in ES6, you could do this:

    // **ES6 ONLY**
    function UpdateTotals() {
    
        // ...
    
        $.ajax({
            // ...
            success: (result) => {  // <==== Arrow function syntax
                // ...
                if (result.Success == 'true') {
                    $(this).attr("data-id", result.Id);
    
                    alert("Saved! " + result.Id.toString());
                } else {
                    alert("Failed");
                }
            }
        });
    
        // ...
    }
    

I'd lean toward #1, because you're doing a lot of repeated $(this) anyway, so just as well to do var input = $(this); once and then use input throughout.

More about this on my blog:


Original answer pre-update:

Since you're using jQuery, you set an attribute with attr, like this:

$(this).attr("data-id", 5);

Live Example:

var input = $("input");
snippet.log("Before: " + input.attr("data-id"));
input.attr("data-id", 5);
snippet.log("After (jQuery): " + input.attr("data-id"));
snippet.log("After (DOM): " + input[0].getAttribute("data-id"));
snippet.log("Element's HTML (after): " + input[0].outerHTML);
<input class="change-handled form-control" type-id="@sub.CategoryTypeId" sub-category-id="@sub.SubCategoryId" data-id="@sub.CategoryBudgetId" style="text-align: right; width: 100%" type="number" value="@(sub.BudgetAmount.HasValue ? sub.BudgetAmount.ToString() : "")" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Or you can just use the DOM directly, by not wrapping the element in a jQuery wrapper:

this.setAttribute("data-id", 5);

Note that in either case, even though you're giving a number as the value, the value will end up being a string (as attributes only store strings).


You'll get people telling you to use data, but data is not just a way to access data-* attributes, even though many people make that mistake. It might be useful for your end goal, though, depending on what that is. The jQuery data function manages a cache of data that it associates with the element. The values data manages are initialized from data-* attributes, but data never writes to data-* attributes. If you're just trying to update attribute values, use attr. If you're trying to do something more complex and it's not important that the values get written back to the element as attributes, look at the docs for data and see whether it might be useful for you. (For one thing, the values data manages can be types other than strings.)

Upvotes: 3

Quentin
Quentin

Reputation: 943569

You are putting your code in the success function where this will be some sort of jQuery ajax object and not an HTML element.

You need to store this in a variable outside the ajax call and then use that variable.

e.g.

var that = this;
$.ajax({

Then:

setAttribute is a DOM method, not a jQuery method.

Either:

$(that).attr("data-id", 5);

or (assuming this is a Element object):

that.setAttribute("data-id", 5);

Upvotes: 3

Related Questions