Javascript - Collection of objects with parent properties in common - javascript

I'd like to create a collection of objects that works like an array. Some time ago, I made this question and I came up with the following solution with the help of the people that helped me:
Newobj.Collection = function(){
Array.apply(this);
for(var i = 0; i < arguments.length; i++){
for(var j = 0; j < arguments[i].length; j++){
this.push(arguments[i][j]);
}
}
return this
}
Newobj.Collection.prototype = Object.create(Array.prototype);
Newobj.Collection.prototype.push = function(o){
Array.prototype.push.call(this, new Newobj.Element(o));
}
However, this leaves the children unconnected from the parent. For example, imagine this collection has a render() function, which makes its children to print some HTML onto the page. Well, I'd like to be able to say something like:
Newobj.Collection.html_container = '#cont';
Newobj.Collection.render = function(){
$.each(this, function(i, el){
el.render()
})
}
Newobj.Element.render = function(){
$(parent.html_container).html('.......')
}
It should be able to set different collections in one page, so make a global container for all the Newobj.Collections is not a solution. This is an example, and I need this for more complex processes than just a render() function.
Anyone has an idea how can I make an array to be able to access a parent class which it is part of?
If the solution could be JSON.stringifyed and be seen as an array on the server side, it would be great too, though it's not the main problem for this question. Right now, if I set a property to the array, it is seen as an object with size > 0 on the server side.
Thank you!

Create reference to collection in element:
Newobj.Collection.prototype.push = function(o){
Array.prototype.push.call(this, new Newobj.Element(o,this));
}
//element constructor gets as second paramater instance of collection
Newobj.Element=function(o,collection){
//this.parent in every element is collection reference
this.parent=collection;
}
Newobj.Element.prototype.render = function(){
$(this.parent.html_container).html('.......')
}
or no reference in Element option:
Newobj.Collection.render = function(){
var parent=this;
$.each(this, function(i, el){
el.render(parent.html_container)
})
}
Newobj.Element.render = function(html_container){
$(html_container).html('.......')
}
But this version need to have methods parameters.

Related

Detect a button and then press it in JavaScript

I want to make a function that would detect a button on a web page and then click it. But I want it to click a specific item.
function imready()
{
var btn = document.getElementsByClassName('text-xxxs mb-02');
for (var i = 0; i < btn.length; i++)
{
if (btn[i].innerText.indexOf('AK-47') > -1)
{
console.log('runtime');
chrome.runtime.sendMessage({ type: 'dontrun', update: 1 }, function (response) {
});
btn[i].click();
pressok();
}
}
How do I make it so that the var "btn" should equal to document.getElementsbyClassName('x') and also a different className ('y')?
Quoting from https://stackoverflow.com/a/29366682/10450049
getElementsByClassName() returns an HTMLcollection object which is similar to an array but not really an array so you can't call
array methods using the returned value. One hack is to use Array's
prototype methods along with .call()/.apply() to pass the returned
object as the context.
var elems = document.getElementsByClassName("royal") ;
var collapsedElems = document.getElementsByClassName("collapsed");
var earray = Array.prototype.slice.call(elems, 0);
var concatenated = earray.concat.apply(earray, collapsedElems) ;
console.log(concatenated)
Demo Fiddle
As far as i understand your question, you can use document.querySelector('.classX.classY') to select the needed button with both classes.
That works for the case if you only need one button on the page selected, from your code i assume exactly that.

Differences between js objects

I got the following problem and I am looking for a really efficient way to do this.
I got two Javascript Objects always build like {id:data,id:data,..}
If I only look on the Keys they will look like this:
B = ["1","2","3"]
A = ["2","3","4"]
Now I need the information what i need to do, to transform B into A, so in this case: Delete B.1 and B.4 = A.4 .
I was thinking that maybe a prototyp function for Object would be a good way to do this.
This is what i have so far:
Array.prototype.diff = function(a) {
return this.filter(function(i) {return a.indexOf(i) < 0;});
};
Object.prototype.syncTo = function(b,callbackA,callbackB){
var a = this;
var bKeys = Object.keys(b);
var aKeys = Object.keys(a);
var toremove = bKeys.diff(aKeys);
var toadd = aKeys.diff(bKeys);
for(var i = 0; i < toremove.length; i++) {
if(b.hasOwnProperty(toremove[i])) {
delete b[toremove[i]];
}
}
callbackB(b);
for(var i = 0; i < toadd.length; i++) {
if(a.hasOwnProperty(toadd[i])){
<<Dont know how to go on now>>
}
}
callbackA(XXXXXX);
};
Where CallbackA should be called with all elements that have to be added to B and CallbackB should be called with all elements that need to be removed from B.
I am struggling With the elements for callbackA and in general whether this is an efficient way of doing this.
Thank you for your support !
EDIT:
An Example for one of the Callbacks would be :
callbackB:
function (items){
for(var i in items){
items[i].removeSomeWhereElse();
}
}
There are a couple of libraries that can do this if your search NPM, as a shameless plug I'll just mention one I authored that diffs any object, including array insertion/deletion/moves:
https://github.com/benjamine/jsondiffpatch
here's the DEMO page diffing 2 arrays, as you need:
http://benjamine.github.io/jsondiffpatch/demo/index.html?desc=moving%20around&left=%5B0%2C1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%5D&right=%5B10%2C0%2C1%2C7%2C2%2C4%2C5%2C6%2C88%2C9%2C3%5D
you can see deletes, adds, and even moves (move detection can be disabled by configuration if you want)
Using library will be the more efficient in saving your time, now if you want to save CPU cycles instead, you could just use a simple implementation of LCS (which is the standard algorithm to solve to the problem you're describing), see: https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
jsondiffpatch includes that (for js), and you can steal it from here: https://github.com/benjamine/jsondiffpatch/blob/master/src/filters/lcs.js

Trouble Returning Value of Clicked Element Javascript

I'm trying my hand at Javascript and interacting with the DOM for the first time by making a simple quiz game. All of my elements are generated dynamically by JS except a few divs.
so here is my problem.
I have
Question 1.
(A)
(B)
(C)
(D)
I want to be able to click on any of the answers and have the values returned. To do that, I wrote the function
function checkCorrectness(element){
element.onclick = function(){
nodes = element.childNodes
for(i = 0; i<nodes.length; i++){
nodes[i].onclick = function(){console.log(nodes)};
}
}
}
//Note answers selectsthe div containing the 4 <p> elements A,B,C,D
checkCorrectness(answers)
Which returns me, as expected, an array of the four elements containing my answers. So, I thought the logical next step would be to select the particular node onClick by changing it by console.log-ing nodes[i] instead of nodes. I would expect this to return me the element which I clicked on, so I could compare its inner HTML to the correct answer, therefore seeing if it was the right answer.
function checkCorrectness(element){
element.onclick = function(){
nodes = element.childNodes
for(i = 0; i<nodes.length; i++){
nodes[i].onclick = function(){console.log(nodes[i])};
}
}
}
checkCorrectness(answers)
However, it just returns undefined. Any help would be much appreciated!
Ah, you've discovered JavaScript closures (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures). This is a very common mistake for those new to JS, so don't feel bad. When you log nodes[i], you're actually accessing nodes at the index for the most recent value of "i" at the time the function executes. Which, in this case, is nodes.length, which is undefined... sorry if that doesn't make sense, but check out the linked article.
You really want something like this:
var logNode = function(val) {
return function() { console.log(nodes[val]) };
};
for(i = 0; i<nodes.length; i++){
nodes[i].onclick = logNode(i);
}
The whole logic of your quiz may be written in a couple of lines
// JavaScript
window.onload = function(){
// This is a simple structure to hold your question_id and the correct answer
// var data = {'10':'B', '11':'D', '12':'A', '13':'A'}; ...
// for this exampple we'll have only one
var data = {'10':'B'};
var li = document.getElementsByTagName('UL')[0].children;
for(i = 0; i < li.length; i++){
li[i].onclick = function(){
if(data[this.parentNode.id] == this.innerHTML){
alert(this.innerHTML + " -> Correct");
}else{
alert('Nope!');
}
};
}
};
// HTML
// Let's say every question has an `id`
// I'd use a list for this case but you may use any other markup
<ul id="10">
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
Working jsBin

Creating an array and storing it in sessionStorage with JavaScript?

I'm doing an assignment that requires us to add objects to a fake cart array from a fake database array, then go to a cart page that displays everything in the "cart." Now, that's all well and good, but for some reason I can't get more than one object to show up in the fakeCart array.
I'm fairly certain the issue is in this function, because everything displays properly otherwise in every way.
So, it turns out I posted code that I was tinkering with. I've since updated it to the almost-working one.
function addToCart(e) {
'use strict';
var fakeCart = [];
for (var i = 0; i < fakeDatabase.length; i++) {
if (fakeDatabase[i].id == e.currentTarget.id) {
fakeCart.push(fakeDatabase[i]);
}
}
sessionStorage.fakeCart = JSON.stringify(fakeCart);
}
Essentially, I can get the code to make a single object go from one array (database) to the other (cart), but whenever I try to add one back in it just replaces the last one.
The code overwrites any existing value of sessionStorage.fakeCart, so there will never be more than one element in the serialized array. You can fix that by reading the value from sessionStorage instead of creating a new list each time.
function addToCart(e, productNum) {
'use strict';
// change this
var fakeCart = JSON.parse(sessionStorage.fakeCart) || [];
for (var i = 0; i < fakeDatabase.length; i++) {
if (fakeDatabase[i].id == e.currentTarget.id) {
// and this
fakeCart.push(fakeDatabase[i]);
}
}
sessionStorage.fakeCart = JSON.stringify(fakeCart);
}
I think :
Instead of
fakeCart[i].push(fakeDatabase[i]);
you require this
fakeCart.splice(i, 0, fakeDatabase[i]);

Generic tree implementation in Javascript

Is anyone aware of a generic tree (nodes may have multiple children) implementation for JavaScript?
It should be able to do atleast these things,
get parent node.
get children nodes.
get all the descendants.
remove all the descendants.
remove children nodes.
Some implementation similar to Adjacency List Model.
Background: I needed JavaScript based hierarchical data storing for my webpage i could not find a good JavaScript implementation of generic trees so what i did is i used ajax to store hierarchical data into database using Adjacency List Model and php. The problem comes when the user is opening the same page in two tabs of same browser or opened the page in two different browsers because both the instances are writing to same table reading from same table which is causing me problems any possible work around for this also answers my question.
Edit: Performance is really not my constraint at any point of time i will not have more than 50 entries.
You can try this: https://github.com/afiore/arboreal
Or this: https://github.com/mauriciosantos/buckets/ (only Binary Searched Trees, but olso other data structures)
If you need anything more sophisticated, you will need to write your own library (or at least one object with all methods you desribed).
EDIT:
This is my simple code to achieve tree functionality. Remove all descendants and remove all children is in fact the same... so:
function Node(value) {
this.value = value;
this.children = [];
this.parent = null;
this.setParentNode = function(node) {
this.parent = node;
}
this.getParentNode = function() {
return this.parent;
}
this.addChild = function(node) {
node.setParentNode(this);
this.children[this.children.length] = node;
}
this.getChildren = function() {
return this.children;
}
this.removeChildren = function() {
this.children = [];
}
}
var root = new Node('root');
root.addChild(new Node('child 0'));
root.addChild(new Node('child 1'));
var children = root.getChildren();
for(var i = 0; i < children.length; i++) {
for(var j = 0; j < 5; j++) {
children[i].addChild(new Node('second level child ' + j));
}
}
console.log(root);
children[0].removeChildren();
console.log(root);
console.log(root.getParentNode());
console.log(children[1].getParentNode());
Run it in Chrome (or other browser which supports console).
Although you did say "generic tree", what your specific requirement sounds simple enough for an already built-in DOMParser.
I respect other programmers' opinions, but still I think you can give DOM a try and see if it fits you.
Here's an simple illustration on how it works:
var tXML="<root><fruit><name>apple</name><color>red</color></fruit><fruit><name>pear</name><color>yellow</color></fruit></root>";
var tree=(new DOMParser).parseFromString(tXML,"text/xml");
//get descendants
var childs=tree.documentElement.childNodes;
for(var i=0;i<childs.length;i++)
{
if(childs[i].nodeName=="fruit")
{
document.write(childs[i].getElementsByTagName("name")[0].textContent);
document.write(": ");
document.write(childs[i].getElementsByTagName("color")[0].textContent);
document.write("<br />");
}
}
//get child node
var appl=tree.documentElement.getElementsByTagName("fruit")[0];
document.write(appl.getElementsByTagName("name")[0].textContent+"<br />");
//get parent node
document.write(appl.parentNode.nodeName);
document.write("<br />");
//remove child node
if(tree.documentElement.getElementsByTagName("color").length>1)
{
var clr=tree.documentElement.getElementsByTagName("color")[1];
clr.parentNode.removeChild(clr);
}
document.write("<textarea>"+(new XMLSerializer).serializeToString(tree)+"</textarea><br />");
//remove descendants
while(tree.documentElement.childNodes.length>0)
{
tree.documentElement.removeChild(tree.documentElement.childNodes[0]);
}
document.write("<textarea>"+(new XMLSerializer).serializeToString(tree)+"</textarea>");
I didn't "simplified" those long function names, so you may get a better idea.

Categories