I've gotten this to work but I would like to know if there is a straightforward way of accomplishing the update. I have a div where a data attribute has been assigned. When the div is clicked, I want one of the properties on the data attribute to be updated.
http://jsfiddle.net/quint/fq7dwsyv/
var originalObj = {
"x": 5,
"y": 58,
"selected": 'no'
};
var originalObjString = JSON.stringify(originalObj); // Convert object to JSON string
$('#test').attr('data-objData', originalObjString); // Attach data attribute to div
$('#test').on('click', function(event) {
var originalData = $(this).attr('data-objData'); // Store data attribute value
var originalDataJSON = JSON.parse(originalData); // Convert string to object
originalDataJSON['selected'] = 'yes'; // Update object property
var updatedData = JSON.stringify(originalDataJSON); // Convert object to string and store
$('#test').attr('data-objData', updatedData); // Update data attribute on div
});
I'd separate the update into its own function
$('[data-example]').click(function() {
function update(object) {
object.foo = 'bar';
return object;
}
var original = this.dataset.example;
original = original && JSON.parse(original) || {};
this.dataset.example = JSON.stringify(update(original));
});
You can store the original object with .data() and mutate it directly without having to store it again with the .data() method:
var originalObj = {
"x": 5,
"y": 58,
"selected": 'no'
};
$('#test').data('objData', originalObj);
$('#test').on('click', function(event) {
$(this).data('objData')['selected'] = 'yes';
console.log($(this).data('objData'));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="test">test me</button>
Remarks
The above solution assumes you are willing to change the way the object is stored with the element. If on the other hand the value must be stored on an element attribute, then there is not much that can be improved in your code. The steps you take are then all necessary (convert from/to JSON, read from attribute, write to attribute, ...etc).
Note however that writing to element attributes is not considered best practice. If anything, you should use .prop() in favour of .attr(): it will result in faster code. But you'll not see the change when consulting the element attributes. In other words, you cannot mix .prop() with .attr().
If there is no room for change in that either, then I would suggest that you ask this question on CodeReview.
Related
I have a list of html elements with data attributes, which I would like to assemble into a jQuery object and manipulate the values.
What is the best way to dynamically add these in an each loop so that I can easily access the data as so: data.name and data.name.prop?
I want all the naming conventions to be dynamic and based on the data.
I based my code on the top answer from here: How to create dynamically named JavaScript object properties?
So far I have:
$('.licences-list .data div').each(function(index) {
var data = {}
cats[$(this).find('p').data('cat')] = $(this).find('p').data('catname')
cats.push(data)
})
But when I try to iterate over the data array, like so:
$.each(cats, function(key, value){
$('<div class="card"><p>'+value+'</p></div>').appendTo('#commercial-licenses');
});
I just get [object Object] output... and I'm not sure why!
var data = {}
cats[$(this).find('p').data('cat')] = $(this).find('p').data('catname')
Each time you loop through, you're actually just adding an empty object (data) to your array (cats). You're then assigning a named property to that array (cats) which $.each has no idea about (it ignores them because it's iterating over an actual array).
My guess is you want an object map which is something like: var cats = { "f1": "feline 1", "f2": "feline " };
In that case what you want is:
var cats = {};
$('.licences-list .data div').each(function(index) {
cats[$(this).find('p').data('cat')] = $(this).find('p').data('catname')
})
If you want an array that contain more values than just strings (or whatever data you have added to the element), you create new objects each time and append them to the cats array:
var cats = [];
$('.licences-list .data div').each(function(index) {
cats.push({
'id': $(this).find('p').data('cat'),
'name': $(this).find('p').data('catname')
});
})
This will then give you an array that you can use $.each over, and access the values using: value.id, value.name
Don't over complicate it.
$('.div').attr('data-attribute', 'data-value');
using your example:
$('.licences-list .data div').attr('attribute-name', 'attribute-value');
However, I want pass an "ID" into the option "value" field with a corresponding string as the option text.
So, if ID for Black = 1, White = 2, Blue = 3, then the html would look something like this:
<option value ='1'> Black </option>
This JSFiddle is similar to what I'm trying to accomplish:
http://jsfiddle.net/e6hzj8gx/4/
Except that I want to send only the value and use a key to call it.
I'm basically building a dropdown with Django that is dependent on what the user selects in another dropdown - there isn't really an elegant way of doing this in Django and it seems that serializing my data to json and then using javascript to build the drop down is the way to go.
My Django data is just a dict:
data = {1: 'Black', 2 = 'White', 3 = 'Blue'}
There are a few ways to loop through a javascript object. When working with a parsed JSON object, you can use:
for (var propName in obj) {
// access data using obj[propName]
}
In more complicated cases, you might have to check if the property isn't inherited from some other prototype using:
if (obj.hasOwnProperty(propName) { /* ... */ }
Furthermore, you can create DOM elements using document.createElement("option")
All together, it'll be something like this:
var obj = JSON.parse(serverData);
for (var propName in obj) {
var jsonValue = obj[propName];
if (jsonValue && (typeof jsonValue === "string")) {
var option = document.createElement("option");
option.value = propName;
option.innerText = jsonValue;
// Add created option to a select element
// ...
}
}
Let me know if I got your question right...
I have a variable being set as the .html(); of a <div />
The variable is the name of another variable. I want it to display the contents of the other variable, however it simply displays the name of the other variable.
How can I force it to show the variable contents rather than just a literal text string?
var clothing = 'dogeshop cryptoshop doge_clothing fyrstikken the_molly_machine peace_and_love moolah_market shibe_swag undieguys urban_graff kravshop got_doge mean_elephant the_dogedoor_store';
setInterval( function() {
var term = $("input").val();
$("#results").html(term);
}, 100);
When the user types 'clothing' into $("input"); the contents of the 'clothing' variable should be displayed in the $("#results"); <div />. Instead it just says 'clothing'.
Another way is to use object properties, like this
var obj = {};
obj.clothing = 'dogeshop cryptoshop doge_clothing fyrstikken the_molly_machine peace_and_love moolah_market shibe_swag undieguys urban_graff kravshop got_doge mean_elephant the_dogedoor_store';
setInterval( function() {
var term = $("input").val();
$("#results").html(obj[term]);
}, 100);
Here's the fiddle: http://jsfiddle.net/ZL7EQ/
The only way I know how to solve this is using eval:
$("#results").html(eval(term))
But you really shouldn't want to do that. :)
Use a dictionary.
// store strings in an object so you can look them up by key
var dict = {
clothing: 'dogeshop ...',
anotherKey: '...'
};
// update the result when the input changes
$('input').on('change', function() {
var result = dict[this.value]; // make sure the key exists
if (result) {
$('#results').val(result); // update the results
}
});
I use to make this mistake myself when working with jQuery.
Try instead:
$("#results").text(term);
Not sure what you have the interval for. You could just change the #results with a change event.
I'm trying to do what I hope is a simple thing -- doing a model.set only on a sub attribute of the object.
Right now, I have a model that looks like this:
{
"attr1" : true,
"attr2" : this.model.get("username"),
"attr3" : $('#tenant_select').val(),
"attr_array": [
{
"id": "sub_1",
"state": "active"
},
{
"id": "sub_22",
"state": "not_active"
}
]
}
I want to be able to grab reach into myMode.attr_array.state and change the value. However, using .set I've only been able to change attributes on the first level, i.e. attr_array.
Is there an way to do this using model.set?
You can do it (I'm wondering why you didn't manage to do it). But you have to be careful:
var array = this.get('attr_array');
array[1].state = 'active';
this.set('attr_array', array);
What's the problem here? Your model holds a reference of the object. Therefore the last line is useless, it won't change anything at all. It's simply equivalent to:
this.get('attr_array')[1].state = 'active';
And you lose any internal stuff Backbone does when you use set.
So what to do? Clone your object:
var array = _.clone(this.get('attr_array'));
array[1].state = 'active';
this.set('attr_array', array); // will trigger 'change' and 'change:attr_array' events
I know I can use $.data but how can I iterate trough all data- attributes and merge the values with the default plugin configuration?
(function($){
$.fn.plugin = function(opts){
opts = $.extend({
foo: 'abc',
boo: 45
}, opts);
return this.each(function(){
...
});
};
})(jQuery);
So if I use
$('.el').plugin();
it should look for data attributes on .el, like data-foo etc ...
In your each() loop, you can merge the object returned by data() with your defaults, then merge the opts argument with the result in a single call to $.extend():
$.fn.plugin = function(opts) {
return this.each(function() {
var thisOpts = $.extend({
foo: "abc",
boo: 45
}, $(this).data(), opts);
// Now use 'thisOpts' as operating parameters for this element...
});
};
This should achieve what you want: the opts argument has the highest priority, followed by the data- attributes of the current element, followed by the plugin defaults.
The .data() method supports 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.
Calling it without speciying a parameter will return an object with key/values pairs for all the data attributes:
var mydata = $("#mydiv").data();
You can get all the attributes for an element like this:
//get the element and setup an array for output
var el = document.getElementById("o"),
arr = [];
//loop through each attribute for the element
for (var i = 0, attrs = el.attributes, l=attrs.length; i < l; i++){
//if the current attribute starts with `data-` then add it to the array
if (attrs.item(i).nodeName.substr(0, 5) == 'data-') {
arr.push(attrs.item(i).nodeName);
}
}
Here is a demo: http://jsfiddle.net/ksbD3/1/
Also I got most of the above code from this answer: Get all Attributes from a HTML element with Javascript/jQuery
You can use data() method on the jQuery object which will give all the data attributes as key/value pair. Try this.
(function($){
$.fn.plugin = function(opts){
//this.data() will give { foo: 'abc', boo: 45 ... }
opts = $.extend(this.data(), opts);
return this.each(function(){
...
});
};
})(jQuery);
.data() reference: http://api.jquery.com/data/
Thank you for all the answers that pointing out using opts = $.extend(this.data(), opts);
One important fact need to be added here: is the transformation of HTML data-attribute.
data-coolName can be accessed like this this.data(coolname)
data-another-cool-name can be accessed like this this.data(anotherCoolName)
Details: Does jQuery internally convert HTML5 data attribute keys to lowercase?
Case sensitive of java and the transformation of the attribute name might be a pitfall if you don't know about.