Will the jQuery $("..").data("key", value) method set the data-key attribute if one is present?
Adam Freeman's Pro jQuery states that it does:
Tip The data method takes the data attributes into account when
setting values as well. When you specify a key, such a [sic] product, the
data method checks to see whether there is a corresponding HTML5 data
attribute, such as data-product. If there is, then the value you
specified is assigned to the attribute. If not, then the data is
stored internally by jQuery.
But I thought that it didn't, and the test that I ran implies that it doesn't. (I checked the errata section—nothing)
Full code is below, but the short of it is that when I set the data-name attribute by calling the attr method, the attribute value changes and can be seen in the chrome elements tab, and retrieved into newValue. When I set it with the data method, neither of these conditions are satisfied; it seems as though using data() always sets the value internally and never on the attribute, even if one is present.
Unfortunately, the docs' only mention of html5 data-attributes is in the section of the data method that takes only a key, and returns the concomitant value; the description of data("key", value) doesn't seem to mention html5 data-attributes at all.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.js"></script>
<script type="text/javascript">
$(function () {
var oldValue = $("#d").data("name");
alert("old value " + oldValue);
$("#d").data("name", "Adam");
//$("#d").attr("data-name", "Adam");
var newValue = $("#d").attr("data-name");
alert("new value " + newValue);
});
</script>
</head>
<body>
<div id="d" data-name="none"></div>
</body>
</html>
I think that Adam Freeman's description is incorrect, or at least not completely accurate.
According to the jQuery documentation:
As of jQuery 1.4.3 HTML 5 data- attributes will be automatically
pulled in to jQuery's data object.
This implies that jQuery pulls these attributes into its own internal representation, rather than overriding the values in the actual attributes.
A quick perusal of the code suggests the same.
According to jQuery's .data() method documentation:
Note that this method currently does not provide cross-platform
support for setting data on XML documents, as Internet Explorer does
not allow data to be attached via expando properties.
It seems it uses the data= if it's there and doesn't throw an error.
Take a look for yourself:
function dataAttr( elem, key, data ) {
// If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if ( data === undefined && elem.nodeType === 1 ) {
var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
data = elem.getAttribute( name );
if ( typeof data === "string" ) {
try {
data = data === "true" ? true :
data === "false" ? false :
data === "null" ? null :
jQuery.isNumeric( data ) ? +data :
rbrace.test( data ) ? jQuery.parseJSON( data ) :
data;
} catch( e ) {}
// Make sure we set the data so it isn't changed later
jQuery.data( elem, key, data );
} else {
data = undefined;
}
}
return data;
}
jQuery's .data() function doesn't interact with the HTML5 data-* attributes at all, other than taking the initial values from them; I'm not entirely sure when this is done, though - another answer suggests it's done at the first call to .data(), which may be correct (it definitely makes sense).
Using .attr() to specify a new value for a data-* attribute doesn't modify the value that jQuery has stored to access using .data(). To illustrate, take a look at this jsFiddle. If you inspect the <div> element and then click on the button, you can see that whilst the attribute on the element has its value changed the two console.log() calls output the same value.
Related
This appears very simple but I cannot see why it's not working. The selector is correct however the div .faqContent is simply not being updated with the data-height attribute.
$('.faqItem .faqContent').each(function(){
var h = $(this).height();
$(this).data('height',h);
});
I have checked that var h is correct, it is in colsole.log as correctly holding the height.
EDIT
It's absolutely not conflict, and console shows no errors.
The data function confuses a lot of people, it's not just you. :-)
data manages jQuery's internal data object for the element, not data-* attributes. data only uses data-* attributes to set initial values, and more, it guesses at what type you want those to be based on what they look like (so something that looks like a number is converted to a number; something that looks like JSON gets converted to an object). The data method never sets data-* attributes on elements, it only sets the data on its internal data object. That means the two (the internal data object and the attribute) get out of sync:
const t = $("#target");
let value;
// Getting the attribute always gets a string
value = t.attr("data-height");
console.log(`${value} (${typeof value})`); // 1 (string)
// Using `.data`, jQuery will guess that because the attribute looks like a number,
// you want it converted to a number
value = t.data("height");
console.log(`${value} (${typeof value})`); // 1 (number)
// `data` only sets the internal data object properties, not the attribute...
t.data("height", 2);
// ...so the attribute still has `"1"`
value = t.attr("data-height");
console.log(`${value} (${typeof value})`); // 1 (string)
// ...even though the data object has 2
value = t.data("height");
console.log(`${value} (${typeof value})`); // 2 (number)
<div id="target" data-height="1"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
If you want to actually set a data-* attribute, use attr:
$(this).attr("data-height", h);
const t = $("#target");
let value;
value = t.attr("data-height");
console.log(`${value} (${typeof value})`); // 1 (string)
// `attr` converts whatever you give it to string
t.attr("data-height", 2);
value = t.attr("data-height");
console.log(`${value} (${typeof value})`); // 2 (string)
<div id="target" data-height="1"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
But if you only want this information for future use, data is fine (assuming you're okay with its automatic type conversion), just don't expect to see it in the DOM inspector, because jQuery doesn't write this information to the DOM.
You will not be able to see it in the element inspector but it is there as jquery set the data attribute internally.
try console.log($(this).data('height'));
.data() is only stores the associated new value in memory(or internally). It'll not change the attribute in the DOM hence you cannot see it updated using inspector tools.
To change the attribute, you can use .attr():
$('.faqItem .faqContent').each(function(){
var h = $(this).height();
$(this).attr('data-height',h);
});
JQuery .data() stores the value on the element itself, it won't add an attribute.
http://api.jquery.com/data/
If you want to add an attribute, use attr:
$('.faqItem .faqContent').each(function(){
var h = $(this).height();
$(this).attr('data-height', h);
});
http://api.jquery.com/attr/
What is the best way to add an attribute to an <input /> on focus using js / jQuery?
Right now, off the top of my head, I would think
$(document).ready(function(){
$('input').focus(function(){
$(this).attr(attributeHere);
});
});
Is that correct? Does the attribute have to have quotes around it?
Right now, it is just going to be an attribute with no value. Value will be added later.
This is what you can do :
$(document).ready(function(){
$('input').on("focus",function() {
$(this).attr('name', 'value'); // value could be '' if you would like to specify it later.
});
});
I hope it helps.
Looks fine to me. Only one thing I would add is that even if you want to leave the attribute empty, you should give it an empty string as a value.
var attrName = 'someAttr';
$(this).attr(attrName,'');
By not passing a value (even an empty string), you are actually calling the getter function for the attribute where you really want to be calling the setter.
You need to do this -
$(this).attr('attributeName','attributeValue');
What you are trying .attr(attributeName) is used to access attribute value
Description: Get the value of an attribute for the first element in
the set of matched elements.
See the api :
http://api.jquery.com/attr/
.attr( attributeName, value )
attributeName
Type: String <-- Either a var containing string or an "string"
The name of the attribute to set.
value
Type: String or Number
A value to set for the attribute.
I've got several html elements that I'm appending hashes to like so:
<p class='message' data-dependencies={'#first':{'equal':'Yes'}}>
Relevant Content
</p>
so that
$(".message").first().data("dependencies")
returns
{'#first':{'equal':'Yes'}}
But as a buddy just pointed out to me, this value is a string. So naturally the filter described below has a hard time with it.
The goal of the filter is to be able to grab elements that have a specified key, in this case "#first".
$el.children().find("*").filter(function(){
var dependency_hash = $(this).data("dependencies");
if(dependency_hash != undefined && "#first" in dependency_hash){
return true
}
});
Is there a way to access the hash as passed via the data object or is there another way I can structure the data so as to accomplish the same means of being able to select elements based on the key?
If you store it as valid JSON, you can parse it, and get is content.
<p class='message' data-dependencies='{"#first":{"equal":"Yes"}}'>
Relevant Content
</p>
var json = $(".message").first().attr("data-dependencies");
// HTML5 browsers
// var json = document.querySelector(".message").dataset.dependencies;
var parsed = $.parseJSON(data);
alert(parsed["#first"].equal); // "Yes"
Or if you use jQuery's .data(), it will parse it automatically.
var parsed = $(".message").first().data("dependencies");
alert(parsed["#first"].equal); // "Yes"
Use JSON.parse. There are polyfills if you need support in older browsers.
$el.children().find("*").filter(function(){
var dependency_hash = $(this).data("dependencies");
var parsed_hash = JSON.parse(dependency_hash);
if(parsed_hash != undefined && "#first" in parsed_hash ){
return true
}
});
You probably want to serialize your data as JSON http://json.org/ and then get it back in JS.
You can use jquery's parser http://api.jquery.com/jQuery.parseJSON/
I would like to store the value of something on my web page for later use by
some javascript functions. I thought of doing something like this where I have
a div with an id of CategoryID and then put the value in that as HTML.
<div id="CategoryID"></div>
$('#categories > li > a').click(function (e) {
e.preventDefault();
$('#CategoryID').html = $(this).attr('data-value')
refreshGrid('Reference');
});
Then later inside functions such as refreshGrid (and some other functions), I could get the value like this:
var categoryID = $('#CategoryID').val();
Does this seem like a good way to store a temporary variable? How about with
HTML5, is there some way to store values without having to put them inside a
div or something like that. All I need is to have a value that is stored in some
way on the page.
One more question. If I store the value is this the correct way to do it:
$('#CategoryID').html = $(this).attr('data-value')
Please use HiddenField for storing variable using jquery just like this:
var tempvalue= $(this).attr('data-value');
$('#my-hidden-field').val(tempvalue);
Hope this is helpful for you.
Use hidden fields on your web-page. For instance.
<INPUT TYPE=HIDDEN NAME="customerId" VALUE="1234567">
And then use .val in JQuery to work with these values.
I'm sometimes using hidden inputs for this purpose depending on the specific case.
<input type="hidden" name="CategoryID" id="CategoryID" />
Then retrieve it just like this:
var categoryID = $('#CategoryID').val();
I use inputs since i feel html which isn't markup for the page shouldn't be there.
Sometimes the easiest thing is to just output the variable from the server into script.
Example with ASP.NET:
<script type="text/javascript">
//<!--
var categoryID = <%= <output value from server goes here> %>;
//-->
</script>
You should try using the localStorage API introduced in HTML5 if you're keen on that otherwise storing it in hidden fields are the way to go.
var idForStorage = $('#CategoryID').val();
window.localStorage.setItem('keyToId', idForStorage);
and to fetch it from localStorage
var fetchedId = window.localStorage.getItem('keyToId');
Note: the localstorage only stores values as Strings, remember that! :)
Also, if you want to be older browser compliant, don't forget to check if localStorage exists and implement a different solution.
Something along the lines of
if(typeof(window.localStorage) !== 'undefined'){
//set item or do whatever
} else {
//implement other solution or throw an exception
}
Good luck!
Just a thought but have you considered storing the value in a javascript variable? If you are only using it in javascript why bother putting it in a hidden field or an element at all?
Just declare you variable in the global scope if your not using namespacing/modules but if you are you can store it in the scope of the module that uses it.
Only considerations with this approach is the variable will be reset on page refresh but if you arnt using the value on the server, just in script then that should be ok.
In general I'd only use a hidden input if the server needed to be able to read it.
EDIT
In reposone to the comments, if you are using your javascript sensibly which includes the use of namespaceing then this approach works with a certain amount of elegance over cluttering up your markup with "variable holders"
A very quick scaffold for namespacing.....
in a file called MyProject.Global.js
//This function is truly global
function namespace(namespaceString) {
var parts = namespaceString.split('.'),
parent = window,
currentPart = '';
for (var i = 0, length = parts.length; i < length; i++) {
currentPart = parts[i];
parent[currentPart] = parent[currentPart] || {};
parent = parent[currentPart];
}
return parent;
}
in a file called MyProject.MyPage.Ui.js
namespace('MyProject.MyPage');
MyProject.MyPage.Ui = function () {
var self = this;
self.settings = {};
function init(options) {
$.extend(self.settings, options);
//any init code goes here, eg bind elements
setValue();
};
};
//this is considered a "private" function
function setValue(){
$('#categories > li > a').click(function (e) {
e.preventDefault();
self.settings.myValue = $(this).attr('data-value');
refreshGrid('Reference');
});
}
function getValue(){
return self.settings.myValue;
}
//any function within this return are considered "public"
return {
init: init,
getValue: getValue
};
};
finally in your page...
$(document).ready(function () {
var ui = new MyProject.MyPage.Ui();
ui.init();
}
then at any point you can get hold of your value using...
MyProject.MyPage.getValue()
The function val() gets the value of an element. But only inputs have values, therefore you should use a hidden input to store the data:
<input id="CategoryId" type="hidden" />
But generelly you can access any attribute as described below. Just be aware that attr() is old and the new function name is prop() -> see Comment for correct explanation regarding attr() and prop()
One more question. If I store the value is this the correct way to do it:
The easier way to use Data- attributes with jQuery is to use the .data() method.
<body id="CategoryData" data-value1="someone" data-value2="sometwo">
....
alert( $("#CategoryData").data('value1') ); // popup with text: someone
alert( $("#CategoryData").data('value2') ); // popup with text: sometwo
Personally, I would rather store all the data associated with items in a wrapper Div or other elements and use jquery.data.
<table data-locationid="1">
<tr data-personid="1">
<td>Jon</td>
<td>Doe</td>
</tr>
<tr data-personid="2">
<td>Jane</td>
<td>Doe</td>
</tr>
</table>
HTML5 data-* Attributes
As of jQuery 1.4.3 HTML 5 data- attributes will be automatically pulled in to jQuery's data object. The treatment of attributes with embedded dashes was changed in jQuery 1.6 to conform to the W3C HTML5 specification.
For browser compatibility, its wiser to use hidden field as suggested by others. But for exploratory reasons, if you would like to use HTML5 it offers WebStorage. For more information please look at http://sixrevisions.com/html/introduction-web-storage/
Also, there is a framework called Lawnchair the offers solution for html5 mobile apps that need a lightweight, adaptive, simple and elegant persistence solution.
There are three ways to store the values as you wish:
Cookies: the good old way. It is supported everywhere but is more suited for values that need to be kept over different pages and it has other limits (such as size, number of entries, etc).
SessionStorage: not supported everywhere, but there are shims. It allows you to store a value that will last for a session only. Example:
sessionStorage.setItem('key', 'value')
sessionStorage.getItem('key', 'value')
This accepts strings only as keys and values. If you want to store objects, you can use JSON.stringify to store them, and retrieve them later with JSON.parse.
LocalStorage: the brother of sessionStorage, but it has no time limit. It allows you to store datas with the same API as sessionStorage and it's kept over time.
If you want to store the value in one part of your JavaScript for access from another part I'd just use a variable:
var categoryID = null; // set some kind of default
$('#categories > li > a').click(function (e) {
e.preventDefault();
categoryID = $(this).attr('data-value');
refreshGrid('Reference');
});
Then later when you need the value just access categoryID directly.
One more question. If I store the value is this the correct way to do it
If you did want to set the value as the content of your div element you'd use jQuery's .html() method or the non-jQuery .innerHTML property:
$('#CategoryID').html( $(this).attr('data-value') );
// or
$("#CategoryID")[0].innerHTML = $(this).attr('data-value');
But if you don't want to use a variable you'd be better off using a hidden input as mentioned in other answers rather than a div.
I'm writing a jquery-plugin, that changes a css-value of certain elements on certain user-actions.
On other actions the css-value should be reseted to their initial value.
As I found no way to get the initial css-values back, I just created an array that stores all initial values in the beginning.
I did this with:
var initialCSSValue = new Array()
quite in the beginning of my plugin and later, in some kind of setup-loop where all my elements get accessed I used
initialCSSValue[$(this)] = parseInt($(this).css('<CSS-attribute>'));
This works very fine in Firefox.
However, I just found out, that IE (even v8) has problems with accessing the certain value again using
initialCSSValue[$(this)]
somewhere else in the code. I think this is due to the fact, that I use an object ($(this)) as a variable-name.
Is there a way arround this problem?
Thank you
Use $(this).data()
At first I was going to suggest using a combination of the ID and the attribute name, but every object might not have an ID. Instead, use the jQuery Data functions to attach the information directly to the element for easy, unique, access.
Do something like this (Where <CSS-attribute> is replaced with the css attribute name):
$(this).data('initial-<CSS-attribute>', parseInt( $(this).css('<CSS-attribute>') ) );
Then you can access it again like this:
$(this).data('initial-<CSS-attribute>');
Alternate way using data:
In your plugin, you could make a little helper function like this, if you wanted to avoid too much data usage:
var saveCSS = function (el, css_attribute ) {
var data = $(el).data('initial-css');
if(!data) data = {};
data[css_attribute] = $(el).css(css_attribute);
$(el).data('initial-css', data);
}
var readCSS = function (el, css_attribute) {
var data = $(el).data('initial-css');
if(data && data[css_attribute])
return data[css_attribute];
else
return "";
}
Indexing an array with a jQuery object seems fishy. I'd use the ID of the object to key the array.
initialCSSValue[$(this).attr("id")] = parseInt...
Oh please, don't do that... :)
Write some CSS and use the addClass and removeClass - it leaves the styles untouched afterwards.
if anybody wants to see the plugin in action, see it here:
http://www.sj-wien.at/leopoldstadt/zeug/marcel/slidlabel/jsproblem.html