function Todo(id, task, who, dueDate) {
this.id = id;
this.task = task;
this.who = who;
this.dueDate = dueDate;
this.done = false;
}
function updateDone(e) {
var spanClicked = e.target;
var id = spanClicked.parentElement.id;
var done = spanClicked.parentElement.done;
spanClicked.innerHTML = " ✔ ";
spanClicked.setAttribute("class", "done");
var key = "todos" + done;
localStorage.setItem(key, "true");
console.log(key);
}
In the second part of the last function I'm trying to change the value of the done property to "true" when the object I'm targeting is clicked for localStorage. What happens instead though is when I check localStorage in the web console the value of that object doesn't change and instead another object is added to the localStorage called "todosundefined: 'true'". I don't understand why when I click an object something is added to the localStorage as a separate, undefined object instead of changing the value of the object I clicked on. Any suggestions to modify it so that I change the done value of the object I click on for localStorage?
As mentioned in the comments above, there's no done property on DOM objects. I'm not entirely sure what you're trying to do, but in order to get the contents of the class attribute of a DOM object, you should use the className property. E.g:
var done = spanClicked.parentElement.className;
Then again, that doesn't appear to make any sense based on the code you posted. I'm assuming you want to use local storage to keep track which items on a list of todo's are done. In that case, wouldn't it be better to just store an array of id's of all items that are done (or the reverse)? You can't store an array directly in local storage, but you can use, for example, JSON.stringify() to get a string representation of the array that you can then store.
Related
You can quite easily set a data attribute of any element with jquery using $('#elid').data('key', value). You can do the same using document.querySelector('#elid').setAttribute('data-key', value)
However, jQuery gives you a special ability that querySelector doesn't - the ability to add attributes of an arbitrary type (including functions, and I think promises, which is what I need).
So if you were to do $('#elid').data('key', function(){console.log('yes')}) with jQuery, and then $('#elid').data('key')(), it would log 'yes' to the console -- we can just assign a function to the element as a data attribute and run it whenever.
But we can't do the same with 'setAttribute' -- when we do it, it apparently just assigns a stringified form of the function to the data attribute, rather than the actual function.
I've provided example code here:
https://jsfiddle.net/8e1wyL41/
So how can I apply data to elements with plain javascript, just like jQuery, including the ability to have arbitrary functions or javascript objects as data attribute values?
jQuery#data() uses an internal object to keep track of data values. It does not update the element to have new or changed data-* attributes when setting data values. When retrieving a data value, if the internal object does not have a set value it will attempt to get it from the data-* attributes.
A overly simplified way of doing this without jQuery would be to just use an object and store your data on that
var element = document.querySelector("div");
element.customData = {};
//get data example, check if customData has a value first, if not use dataset
var someData = element.customData["somedata"] || element.dataset["somedata"];
//set
element.customData["somedata"] = function(){};
If you don't want to contaminate the element with arbitrary properties you could use a WeakMap, pending on browser support, to associate a data object with the element. This also allows for using a single object to maintain other element data objects as well. The key to the data object is the element object itself. And the data object will get deleted from the map automatically once the element is garbage collected
var dataMap = new WeakMap();
var element = document.querySelector('div');
var elementData = dataMap.get(element);
if(!elementData){
dataMap.set(element, elementData = {});
}
//get data example, check if data object has a value first, if not use dataset
var someData = elementData["somedata"] || element.dataset["somedata"];
//set
elementData["somedata"] = function(){};
.dataset sets or gets a DOMString of HTML data-*, though you can use Function() to call the function stored as string at HTMLElement.dataset
document.documentElement.dataset.fn = function fn(...args) {console.log(args)};
new Function("return " + document.documentElement.dataset.fn)()("yes");
I'm creating a game bot on telegram using node js.
Currently I'm facing a problem on shared variable (module.exports). I'm storing some of the data on the variable. And the problem is, the shared variable index always change. For example, please refer to my code below
var sharedVar = [];
createNewRoom = function(res) {
var index = sharedVar.length;
sharedVar.push({ groupId : res.chat.id }); // every time this function is invoked, it will create a new array inside sharedVar object
//Here comes the problem, it's about the index,
//because I'm using sharedVar to store arrays, then it will become a problem,
//if one array is deleted (the index will change)
var groupId = sharedVar[index].groupId; // it runs OK, if the structure of array doesn't change, but the structure of array change, the index will be a wrong number
}
As you can see, i got callGameData function, when i call it, it will show the last value of sharedVar, it's supposed to show the current room values / data.
As i mention on the code above, it's all about the dynamic array in the sharedVar object, the index will change dynamically
Any thoughts to tackle this kind of issue? I was thinking about using a new sharedVar object everytime the createNewRoom function is invoked, but the thing is, i have to use sharedVar in many different function, and i still can't figure it out on using that method.
EDIT
This is the second method
var gameData = undefined;
createNewRoom = function() {
this.gameData = new myConstructor([]); // it will instantiate a new object for each new room
}
myConstructor = function(data) {
var _data = data;
this.object = function() {
return _data;
}
}
callGameData = function() {
console.log(gameData);
}
An array is fundamentally the wrong data type to use if you want to keep indices the same even in the face of removing entries.
A better method is to use properties of an object. For example:
var roomCache = { nextId: 1 };
createNewRoom = function(res) {
roomCache[roomCache.nextId++] = {groupId: res.chat.id}; // Add a new object to the cache and increment the next ID
}
After adding two elements, you'll have the rooms in roomCache[1] and roomCache[2] - if you want to start at zero just change the original value of nextId. You can delete elements in this object and it won't shift any keys for any other objects - just use delete roomCache[1] for example to get rid of that entry.
This assumes there isn't a better ID out there to use for the cache - if, for example, it made more sense to lookup by res.chat.id you could certainly use that as the key into roomCache rather than an auto-incrementing number. Here's how it would look like to cache the values by the group ID instead:
var roomCache = { };
createNewRoom = function(res) {
roomCache[res.chat.id] = {groupId: res.chat.id}; // Assumes res.chat.id is not a duplicate of an already cached obhect
}
Now you could just look up by group ID in the cache.
Yes, it's definitely a problem cause you are not keeping track of the index in a logical way, you are relying on position on the array which it changes, you need something that doesn't change over time to keep consistency and supports deletition of the element without affecting the rest of the elements. You could use mongo to store the generated rooms by id or maybe redis or some kind of key value pair database to store that kind of information.
I am in a strange condition. I have an array of objects, I used angular.forEach to modify each object price key value but when I am changing it in each it is also changing main array object as well.
Have a look on code, you will understand then what I am trying to say.
var option_1_val = $scope.options.option_1_val;
var option_2_val = $scope.options.option_2_val;
console.log('genies',sc.genies);
var new_arr = [];
var each ;
each = sc.genies;
angular.forEach(each,function(val,key){
var ob = {};
ob = val;
var priceA = angular.fromJson(ob.price);
console.log('price',priceA);
var option = option_1_val.replace(" ","-")+","+option_2_val.replace(" ","-");
console.log(option);
ob.price = priceA[option];
console.log(ob);
new_arr.push(ob);
});
option = 'Non-Vegetarian,' (after calculating)
sc.genies = [{"gs_id":"3","user_id":"25","service_id":"7","price":"{\"Vegetarian,Bengali\":\"200\",\"Vegetarian
,Chinese\":\"3100\",\"Vegetarian,Gujarati\":\"800\",\"Vegetarian,Italian\":\"100\",\"Vegetarian,Maharashtrian
\":\"100\",\"Vegetarian,Punjabi\":\"100\",\"Vegetarian,-South-Indian\":\"300\",\"Vegetarian,Thai\":\"100
\",\"Non-Vegetarian,Bengali\":\"1100\",\"Non-Vegetarian,Chinese\":\"3100\",\"Non-Vegetarian,Gujarati
\":\"100\",\"Non-Vegetarian,Italian\":\"100\",\"Non-Vegetarian,Maharashtrian\":\"100\",\"Non-Vegetarian
,Punjabi\":\"100\",\"Non-Vegetarian,-South-Indian\":\"80\",\"Non-Vegetarian,Thai\":\"100\",\"Jain,Bengali
\":\"2100\",\"Jain,Chinese\":\"2100\",\"Jain,Gujarati\":\"4100\",\"Jain,Italian\":\"100\",\"Jain,Maharashtrian
\":\"100\",\"Jain,Punjabi\":\"100\",\"Jain,-South-Indian\":\"800\",\"Jain,Thai\":\"100\"}","min_price"
:"80","max_price":"4100","username":"abdul quadir","email":"abdul.quadir#kiozen.com","rating":"3"}]
now when I am repeating sc.genie, I have taken it in a new variable already "each" and then I am changing "price" key of each array to undefined but strange point is when I see in console value of price in sc.genies is also changed to "undefined". Huh!
I hope you got my point, please help me why is that happening.
Thanks
You should use angular.copy then when change in each value not affect to original value. because of angular.copy assign old value in new variable without reference.
like:
var each ;
each = angular.copy(sc.genies);
instead of
each = sc.genies;
There is a simple answer. The reason why "both" values changes, is because it is actually the same object. The variable val from this line angular.forEach(each,function(val,key){ ... contains a pointer to an object. It is not another object. It is the same object, it is only accessed via different variable name.
If you really want the original and working copy to be different objects, then you need to manually create new instance with the same values.
You can create copy of an object like this (good for simple objects):
var copy = JSON.parse(JSON.stringify(originalObject));
or as pointed in the comment above, you can use angular.copy(source, destination). See the documentation https://docs.angularjs.org/api/ng/function/angular.copy
Is it possible with the HTML data- attributes to hold a reference to another DOM element? For example, I could do this with jQuery:
var domel1 = document.getElementById("#mydiv")
var domel2 = document.getElementById("#mydiv2")
$(domEl1).attr('data-domel', domel2)
Then later on, with jQuery I would do:
var domel1 = document.getElementById("#mydiv")
var domel2 = $(domel2).data('domel')
$(domel2).html("blahblahblah")
This might seem like a trivial example because I could just reference domel2 with the same id like I did at first, but there are cases where this could be useful for representing relationships between <div>s.
Yes and no. You cannot store a reference to a DOM element in a data- attribute. However, you can associated a reference to a DOM element to another element using jQuery .data(), which are already using:
$someElement.data('name', someOtherElement);
From the jQuery documentation:
The .data() method allows us to attach data of any type to DOM
elements in a way that is safe from circular references and therefore
from memory leaks.
Note that using .data() to set data will add it to the data store but not add it as a data- attribute in the DOM. However, using .data() to read data will check the data store as well as the data- attribute (if one exists and there's no data store value with the given key).
Not directly. data-* attributes are just attributes, so you can only store a string in them.
But, of course, you can store the id or class of your target element, in order to retrieve it later.
Or you could also store a reference to the element in a property, since properties can have any value.
Not legal, since attributes should be text strings. But since you're using jQuery you could use the .data() method instead.
jQuery .data() basically does everything for you.
But if you cannot use jQuery, or have to implement something more case-specific, you can hold indexes to a global object that holds the actual data.
This way you can support any type of data you need, references, objects, functions (even binded functions).
Here's a vanilla implementation of data, though I'm not sure what are your limitations - you will probably want to change some bits of the code below.
Note that elements in this code are identified by using their id.
// Set data with: elem.data('key',anything)
// Get data with: elem.data('key')
// Remove data (kind of) with: elem.data('key',undefined)
// This will generate random id on element if id is missing
Node.prototype.force_id = function() {
return this.id || (this.id = ('' + Math.random()).replace('0.', 'id-'));
}
// Our own data implementation
window.DATAOFDOM = {}; // I like naming globals as ALLCAPS
Node.prototype.data = function(k, v) {
if (arguments.length == 1) {
// getter
if (window.DATAOFDOM[this.id]) {
return window.DATAOFDOM[this.id][k]; // returns undefined when k isn't there
}
// else: implicitly returns undefined when there's no data for this element
} else {
// setter
this.force_id();
if (!window.DATAOFDOM[this.id])
window.DATAOFDOM[this.id] = {};
return window.DATAOFDOM[this.id][k] = v;
}
}
https://jsfiddle.net/oriadam/63zn9qtd/
This is not an answer because no element can store DOM element as an attribute value. Here is a small polyfill to do the same
If I have the same requirement I would follow this approach,
var DataDOM = (function(){
function DataDOM(){}
var elements = {}, counter = 0;
DataDOM.prototype.set = function(ele){
elements['ele' + counter] = ele;
counter += 1;
return ele + (counter - 1);
}
DataDOM.prototype.get = function(eleRef){
return elements[eleRef];
}
return DataDOM;
})();
Use like below
var dDOM = new DataDOM();
For example if if I want to set DOM reference to a element data attribute
var div = document.getElementById('someId');
var attr = dDOM.set(div);
Then set attr as data to some element
then while retrieving use below method to get it back
var referedElement = dDOM.get(someElement.attr('data'));
Because there is no direct way to store elements as Data AFAIK.
In my Javascript drag and drop build app, a variety of buildings can be built. The specific characteristics of these are all saved in one object, like
var buildings = {
house: ['#07DA21',12,12,0,20],
bank: ['#E7DFF2',16,16,0,3],
stadium: ['#000000',12,12,0,1],
townhall: ['#2082A8',20,8,0,1],
etcetera
}
So every building has a number of characteristics, like color, size, look which can be called as buildings[townhall][0] (referring to the color). The object changes as the user changes things. When clicking 'reset' however, the whole object should be reset to its initial settings again to start over, but I have no idea how to do that. For normal objects it is something like.
function building() {}
var building = new building();
delete building;
var building2 = new building();
You can easily delete and remake it, so the properties are reset. But my object is automatically initialized. Is there a way to turn my object into something that can be deleted and newly created, without making it very complicating, or a better way to store this information?
You can keep initial state as a prototype on an object.
var Base = function(){};
Base.prototype={a:1,b:2};
var c = new Base();
Now, you can change value of a or b to whatever you want.
To restore, just use
delete c.a or delete c.b
You will get your initial value back.
Hope this help.
Just use a copy/clone method to restore the original state
var defaults = {
foo: "bar"
};
var building;
function reset(){
building = {};
for (var key in defaults) {
if (defaults.hasOwnProperty(key){
building[key] = defaults[key];
}
}
}
Now you can just call reset() whenever you need to have building reset.