Getting and Setting a data attribute in HTML - javascript

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.

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:
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");
}
}
});
// ...
}
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
});
// ...
}
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:
You must remember this
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.)

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);

Related

jQuery argument suddely changes into n.Event object

I have a dropdown on my page,which selected value I take like this:
<select id="campaign" class="form-control">
<option>Choose campaign</option>
<option value="createCampaign">Create new campaign</option>
<?php while($row=$resultForCampaigns->fetch_assoc()){
$campaignName=$row['campaign_name'];
echo "<option value=$campaignName>$campaignName</option>";
}?>
</select>
var campaign = $('#campaign option:selected').val();
But when I'm passing that same campaign value as a argument of new function, and do console.log(campaign) it says:
Here is the whole code:
$(document).ready(function() {
$('#campaign').change(function() {
var campaign = $('#campaign option:selected').val();
console.log(campaign);
if (campaign != 'Choose campaign') {
console.log(campaign);
$('#deleteCampaign').click(function(campaign) {
console.log(campaign);
var r = confirm("Are you absolutely sure you want to delete selected campaign?");
if (r == true) {
var data = {};
data.action2 = "deleteCampaign";
data.campaign = campaign;
$.ajax({
url: "../includes/adapter.php",
type: "POST",
dataType: "JSON",
data: data,
async: true,
success: function() {
if (data) {
console.log(data);
$('#poruka').append('<div class="alert alert-success"><strong>Success!</strong> You have successfully deleted campaign!</div>');
} else {
$('#poruka').append('<div class="alert alert-danger"><strong>Failure!</strong> Something went wrong with deleting your campaign! Please try again</div>');
}
}
});
}
});
}
});
});
So, console.log(campaign) after click on $('#deleteCampaign') turns value of campaign from one that I've gave it, to one that picture represents. Really don't know what's going on, so If anyone could explain me how to get correct value inside function that is tiggered by click, I would be very thankful.
The variable campaign is redefined when you use the same name as a function argument in a lower scope.
Variables are scoped to functions, and function arguments are considered variables, it would be the same as doing
var something = 'stuff';
function go() {
something = 'other stuff';
console.log(something); // obviously "other stuff"
}
The first argument for the click function in jQuery is the event object, you can't pass in anything else.
All you have to do is just remove the argument.
var campaign = $('#campaign option:selected').val();
$('#deleteCampaign').click(function() {
console.log(campaign); // still the value

Javascript - unable to call function from another file

I'm trying to call a function from one file in another js file.
general.js
function delete_post(post_id, post_type, nonce) {
$.post(Ajax.ajaxurl, { action: 'delete_post', post_id: post_id, nonce: nonce, post_type: post_type}, function (data) {
var result = $.parseJSON(data);
if (result.status == 'error') {
$('#post_'+post_id).prepend('<div class="alert alert-danger">' + result.message + '</div>');
}
if (result.status == 'success') {
$('#post_'+post_id).fadeOut(1000, function(){
$(this).remove();
});
}
});
}
details.js
$('body').on('click', '.remove-row', function (e) {
e.preventDefault();
var post_id = $(this).attr('data-target');
var nonce = $(this).attr('data-nonce');
var parent_id = $(this).attr('data-parent');
var post_type = $(this).attr('data-post_type');
bootbox.confirm(Ajax.are_you_sure, function(result) {
if (result) {
delete_post(post_id, post_type, nonce);
}
});
});
On the page they are loaded in the correct order:
<script type='text/javascript' src='http://domain.com/js/general.js?ver=3.9.1'></script>
<script type='text/javascript' src='http://domain.com/js/details.js?ver=3.9.1'></script>
However, when I click on the remove-row button, I get Uncaught ReferenceError: delete_post is not defined.
What am I missing?
That error tells us that you haven't shown all of general.js, and in particular that your function declaration for delete_post is inside another function (one possible example below). So it's not a global.
If you want to make it a global, you can do that by putting this line in general.js:
window.delete_post = delete_post;
The properties of the window object are globals.
In general, I would recommend keeping globals to a minimum, so you might want to have a single global object you use for all your stuff, along these lines:
if (!window.myApp) {
window.myApp = {};
}
window.myApp.delete_post = delete_post;
...and then instead of
delete_post(post_id, post_type, nonce);
...in your other file, use
myApp.delete_post(post_id, post_type, nonce);
When I say it's inside another function, here's one example of that:
$(document).ready(function() {
function delete_post() {
// ...
}
});
Your example may look slightly different, but that's the gist.

Assign value to id in html link with javascript

I have the following link that passes an id to my controller to render a report.
Print Report
I would like to assign this value id in the link above to the result I have returned from the database in the call below....
// Global variable
var DocumentID = null;
$('#Save').click(function(e) {
if (document.forms[0].checkValidity()) {
e.preventDefault();
$.ajax({
url: "/Home/SaveDetails",
dataType: "json",
type: "POST",
data: ko.toJSON(viewModel),
contentType: "application/json;charset=utf-8",
success: function(result) {
if (result > 0) {
//Assign return value to DocumentID
DocumentID = result;
alert("This work request has been successfully saved in database. The Document ID is: " + result);
} else {
alert("The Work Request was not saved, there was an issue.");
}
},
complete: function() {
// Hide loading image.
},
error: function(jqXHR, textStatus, errorThrown) {
// Handle error.
alert(errorThrown);
}
});
} else {
alert("Form is not valid");
}
});
The call above works and returns a DocumentID and displays this in an alert with success. The link above works if the id has a hard-coded value; I would just like to make this value dynamic.
if (result > 0) {
//Assign return value to DocumentID
var DocumentID = result;
var href = '#Url.Action("GetReport", new { type = "PDF", id =' + DocumentID +'})';
$("a:contain('Print Report')").attr('href', href);
}
The above suggestions will generate an error for an unsupported pseudo, in this case 'contain' which is not available or known to the anchor tag. There is also an error with the syntax of putting the DocumentID in the Url.Action. I was not able to get this to work, instead I found another error: Too many characters in literal.
Instead give your anchor an id and find that id using jQuery and replace it's href attribute, this is done in the second line of code under Javascript.
html:
<a href="#" **id="Report"**>Print Report</a>
Javascript:
var DocumentID = result; var href = '#Url.Action("GetReport")?type="PDF"&id=' + DocumentID + '';
$("#Report").attr('href', href);
Take note of two things. The URL is formed and the anchor tag is found differently versus previous suggestions.
This has been tested and works for parameters of type string and int, respectively in your callback function to handle getting the report. If you want two strings then add an additional double-quote around the DocumentID like this: id=" ' + DocumentID + ' "';
in your ajax-success handler just set the href of the anchor to whatever you want
var newHref = '#Url.Action("GetReport", new { type = "PDF", id = '+DocumentID +'})';
$('a').attr('href',newHref);

Convert function into plugin

I have a function that I call multiple times in my projects:
function fillSelect(select) {
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Data.asmx/Status",
dataType: "json",
async: true,
success: function(data) {
$.each(data.d, function(i) {
select.append('<option value=' + data.d[i].value + '>' + data.d[i].name + '</option>');
});
},
error: function(result) {
alert("Error occured. Contact admin");
}
});
}
Then in my code I'm using this like so:
fillSelect($('select#status1'));
fillSelect($('select#status2'));
fillSelect($('select#status3'));
What I would like to do is to convert my function into plugin, so I would be able to call it as so:
$('select#status1, select#status2, select#status3').fillSelect();
Using http://starter.pixelgraphics.us/ I've generated empty schema:
(function($) {
$.ajaxSelect = function(el, select, options) {
// To avoid scope issues, use 'base' instead of 'this'
// to reference this class from internal events and functions.
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Add a reverse reference to the DOM object
base.$el.data("ajaxSelect", base);
base.init = function() {
base.select = select;
base.options = $.extend({}, $.ajaxSelect.defaultOptions, options);
// Put your initialization code here
};
// Sample Function, Uncomment to use
// base.functionName = function(paramaters){
//
// };
// Run initializer
base.init();
};
$.ajaxSelect.defaultOptions = {
clear: false //append to select or replace current items
};
$.fn.ajaxSelect = function(select, options) {
return this.each(function() {
(new $.ajaxSelect(this, select, options));
});
};
})(jQuery);
but I don't know how to fill it.
What I would like to do is to call sever ones and then fill as many select items as I put in parameters.
Is all that code really necessary for such a small plugin?
I know that there are probably some plugins that this functionality, but I would like to create my own plugin, just to learn a bit more :)
You don't need all that boiler plate you could do as below
$.fn.fill = function fillSelect(options) {
var self = this;
options = $.extend({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "Data.asmx/Status",
dataType: "json",
async: true,
success: function(data) {
var list = "";
$.each(data.d, function(i) {
list += '<option value='
+ data.d[i].value + '>'
+ data.d[i].name
+ '</option>';
});
self.filter("select").each(function(){
$(this).append(list);
});
},
error: function(result) {
alert("Error occured. Contact admin");
}
},options);
$.ajax(options);
return this;
}
the first thing to notice that the function is added to the jQuery prototype/$.fn. Then the success handler have been changed so that all selected elements will be handled and lastly the selection is returned to make chaining possible, as this is usually expect when using jQuery.
The above code will append the same options to all selected "select" elements only. If you select something else the options will not be appended to those elements.
I've changed the signature to accept an options element. In the above version there's default vesrion equaling your ajax options. If other values are supplied, they will override the default ones if a default exist. If a default does not exist the values will be added to the options object
You just need to add your method to the $.fn object, as described here: http://docs.jquery.com/Plugins/Authoring
The this keyword will evaluate to the jQuery selector that was used to invoke your function's code, so instead of using the select parameter in your code, just use this

Advice requested - passing variables between functions using json/jquery & ajax

I've looked over a lot of 'similar' q&a threads on SO but to be honest, as I don't have too much of a grip on js programming, I'm finding it difficult to make sense of a lot of the answers (as far as they may apply to my own situation).
The context is this, I have two php scripts one returning a list of customer_ids (json encoded) for a set period and the other returning their preferences for news feeds (json encoded).
I wrote the following, having googled a bit to get a basic understanding of how to setup an ajax function in jQuery:
$('document').ready(function() {
$.ajax({
type:'GET', url: 'cust_selection.php', data: '',
succes:function(cstmrid) {
var clistlen = cstmrid.length;
var i=0;
var cstmr;
for( ;cstmr=cstmrid[i++]; ) {
$('#adminPanel>ul>li').append("<a href='' onclick='alert("+cstmr+")' class='lst_admin basic'>"+cstmr+"</a>"); //alert to be replaced with a function call which passes customerid to the function below.
}
},
dataType:'json'
});
var cstmrid = "483972258"; //hardcoded for testing purposes
$.ajax({
type:'GET', url:'newsfpref.php?', data:'cref='+cstmrid,
success:function(npfdata) {
var item;
var n=0;
for( ;item=npfdata[n++]; ) {
var news = npfdata[n].nsource;
$('#adminMain>table>tbody').append("<tr><td>"+item+"</td></tr>");
}
},
dataType:'json'
});
});
Now from the first ajax function, I get a list of links which I want to be able to click to launch the second ajax function and pass it the customer id so that it can grab a list of the news sources that they've configured for their pages.
The alert and the hard-coded customer id both suggest that the functions are 'working', but when I try and adjust the first function so that:
...
$('#adminPanel>ul>li').append("<a href='' onclick='getCustomerNP("+cstmr+")' class='lst_admin basic'>"+cstmr+"</a>");
... is calling a modified version of the second function, as below:
...
function getCustomerNP(cstmrid) {
$.ajax({
type:'GET', url:'newsfpref.php?', data:'cref='+cstmrid,
success:function(nprfdata) {
var item;
var n=0;
for( ;item=npfdata[n++]; ) {
var news = npfdata[n].nsource;
$('#adminMain>table>tbody').append("<tr><td>"+item+"</td></tr>");
}
},
dataType:'json'
});
}
Everything seems to just fail at this point. The second function doesn't seem to 'receive' the variable and I'm not sure if it's something elementary that I've overlooked (like some muddled up " and ' placements) or if what I am trying to accomplish is actually not the way jQuery ajax functions interact with each other.
As you can see, I've cannibalised bits of code and ideas from many SO q&a threads, but copying without much of an understanding makes for a frustratingly dependent life.
I would appreciate as much - expansive - comment as you can provide, as well as a solution or two (naturally).
EDIT: Not to confuse anyone further, I've been modifying the above and correcting my (many) errors and typos along the way. At present, the code looks like below:
$('document').ready(function () {
$.ajax({
type: 'GET', url: 'cust_selection.php', data: '',
succes: function (cstmrid) {
var clistlen = cstmrid.length;
var i = 0;
var cstmr;
for (; cstmr = cstmrid[i++]; ) {
var a = $("<a href='' class='lst_admin basic'>" + cstmr + "</a>").click(function () {
getCustomerNP(cstmr)
})
$('#adminPanel>ul>li').append(a); //alert to be replaced with a function call which passes customerid to the function below.
}
},
dataType: 'json'
});
function getCustomerNP(cstmr) {
alert(cstmr);
}
});
You've got a typo in the $.ajax() success function within getCustomerNP(). The function declaration:
success:function(nprfdata) {
... has a parameter nprfdata, but then within the function you use npfdata (missing the r).
Also this code:
var item;
var n=0;
for( ;item=npfdata[n++]; ) {
var news = npfdata[n].nsource;
$('#adminMain>table>tbody').append("<tr><td>"+item+"</td></tr>");
}
...declares and sets variable news that you never use. And it doesn't seem right to increment n in the for test expression but then use n within the loop. You never set item to anything but you use it in your .append().
(Note also that JS doesn't have block scope, only function scope, so declaring variables inside an if or for loop doesn't limit them to that if or for block.)
I would not create inline onclick handlers like that. I'd probably do something more like this:
$('#adminPanel>ul>li').append("<a href='' data-cstmr='"+cstmr+"' class='lst_admin basic'>"+cstmr+"</a>");
...and then within the document ready setup a delegated event handler to catch the clicks on those anchors:
$('#adminPanel>ul>li').on('click', 'a.lst_admin', function() {
$.ajax({
type:'GET', url:'newsfpref.php?', data:'cref='+ $(this).attr('data-cstmr'),
success:function(npfdata) {
var item,
n=0,
// cache the jQuery object rather than reselecting on every iteration
$table = $('#adminMain>table>tbody');
// increment n only after the current iteration of the loop
for( ;item=npfdata[n]; n++) {
// change to use item
$table.append("<tr><td>"+item.nsource+"</td></tr>");
}
},
dataType:'json'
});
});
As you append your like with <a href='' onclick='getCustomerNP("+cstmr+")', Make sure you can access the function getCustomerNP.
Try to define getCustomerNP as
window.getCustomerNP = function(cstmrid) {
...
If you defined it in the $(document).ready(function(){ ... }) block, try this
$('document').ready(function () {
$.ajax({
type: 'GET', url: 'cust_selection.php', data: '',
succes: function (cstmrid) {
var clistlen = cstmrid.length;
var i = 0;
var cstmr;
for (; cstmr = cstmrid[i++]; ) {
var a = $("<a href='' class='lst_admin basic'>" + cstmr + "</a>").click(function () {
getCustomerNP(cstmr)
})
$('#adminPanel>ul>li').append(a); //alert to be replaced with a function call which passes customerid to the function below.
}
},
dataType: 'json'
});
function getCustomerNP(cstmrid) {
$.ajax({
type: 'GET', url: 'newsfpref.php?', data: 'cref=' + cstmrid,
success: function (nprfdata) {
var item;
var n = 0;
for (; item = npfdata[n++]; ) {
var news = npfdata[n].nsource;
$('#adminMain>table>tbody').append("<tr><td>" + item + "</td></tr>");
}
},
dataType: 'json'
});
}
});

Categories

Resources