I created an algorithm for displaying the title of an element in a certain block, but I can't delete what I output.
js:
var products = document.querySelectorAll('.product-block'); //objects
var product_names = document.querySelectorAll('.product-block h5'); //headings which we should output
var array_product_names = Array.prototype.slice.call(product_names); //from NodeList to array
var favourite_elements = document.querySelector('.favourite_elements'); //block where we should output
//method of determining which button is pressed
products.forEach((product, i) => {
product.onclick = function () {
if (product.style.background != "orange") {
product.style.background = "orange";
var favourite_element = array_product_names[i].outerHTML;
favourite_elements.insertAdjacentHTML('afterbegin', favourite_element); //adding elements
}
else if (product.style.background == "orange") {
//here we should delete heading of determined object from list in block, but i don't know how
}
}
});
This is oversimplified but it shows how you can remove items from a list:
let a=[1,2,3,4,5];
let toDelete=[2,3];
a=a.filter(e=>!toDelete.includes(e))
Now,the value of a would be:
[1,4,5]
Based on that idea, you could do more abstract things such as:
Array.prototype.filterA = function(f){
return this.filter(e=>f(e));
}
And from then on you would just filter elements like this:
[{color:"orange"},{a:1},{b:{c:2}}].filterA(e=>e.color!="orange")
Related
So I have a dynamic variable that could be any integer from 5 to < 99 decided by the user.
var topLevelMenusTotalNum
Each top level menu has 5 fixed properties that I want to store which are mostly integers and some long numbers. And then recall for use in my bit of code.
Whats is the bext way for me to store these values in a storage system that can be dynamic in size?
I'm guessing I should store each top level menu as an object, with the 5 properties, i.e.
menu1.property1 = 500
menu1.property2 = 23
...
menu1.property5 = 24.3445345644
But how can I dynamically create the menu1, menu2, menu3 etc objects depending on how many the user has created?
Should I create the objects in an array? Or something else?
Does the order matter? If so, use an array.
Does the name matter? If so, use an object with named properties.
Do neither matter? Then it doesn't really make a difference. Arrays are slightly easier to loop over.
if you have an object, you can dynamically add items to it like so:
var menus = {
"menu1": {
//properties
},
"menu2": {
//properties
} //etc...
}
then you could add to it like so:
menus['menu' + newMenuNR] = {'property1': 21, 'property2': 10} //<-- properties in there
this is fully dynamic and won't result in problems later on, to loop through the object, you could use a 2-dimentional loop.
for(menu in menus) {
for(item in menu) {
alert(item.['property1']); //displays property of item in menu in menus (replace property with your own property names)
}
}
I will suggest you to use an object for top layer and each object will contain array as class member.
Object will help you to create dynamically based on user and each object of user will contain an array which has five properties.
I would suggest doing it with objects as long as the parameters can be named.
If you need a lot of objects, just keep an array of objects to search/sort/filter.
//Class (Hide this in some library script)
var Menu = (function () {
function Menu(parameters) {
if (parameters === void 0) { parameters = {}; }
this.node = document.createElement("ul");
this.items = [];
this.width = 100;
this.height = 100;
this.name = "";
this.title = "";
this.children = [];
//Apply parameters
for (var key in parameters) {
if (parameters.hasOwnProperty(key) && this.hasOwnProperty(key)) {
this[key] = parameters[key];
}
}
//Apply node parameter
this.node.title = this.title;
//Add to static
Menu._menus.push(this);
}
Menu.prototype.render = function () {
//Reset contents
this.node.innerHTML = "";
//Append sub-menues
for (var childIndex = 0; childIndex < this.children.length; childIndex++) {
var child = this.children[childIndex];
var li = document.createElement("li");
li.appendChild(child.render());
this.node.appendChild(li);
}
//Append generic items
for (var itemIndex = 0; itemIndex < this.items.length; itemIndex++) {
var item = this.items[itemIndex];
var li = document.createElement("li");
li.innerHTML = item;
this.node.appendChild(li);
}
//Return node
return this.node;
};
Menu._menus = [];
return Menu;
}());
//Implementation
//create menu
var topMenu = new Menu({ items: ["Just testing"], title: "Super!" });
//Add anonymous submenues
topMenu.children
.push(new Menu({ items: ["item 1"], title: "sub", name: "sub1" }), new Menu({ items: ["item 3", "item 2"], title: "sub", name: "sub2" }));
//Add to "bigList"
document.getElementById("bigList").appendChild(topMenu.render());
//Updates incoming
setTimeout(function () {
//Find menu with the most items + children (Which is easy with named parameters)
var m = Menu._menus
.filter(function (a) {
return a.title == "sub";
}).sort(function (a, b) {
return (b.items.length + b.children.length) - (a.items.length + a.children.length);
})[0];
//Add new item
m.items.push("Sweet right?");
//Update node
m.render();
}, 2000);
setTimeout(function () {
//Find last menu
var m = Menu._menus
.reverse()[0];
//Remove last item
m.items.splice(m.items.length - 1, 1);
//Update node
m.render();
}, 4000);
<div id="bigList">
Named parameters are lovely :-)
</div>
I am doing the below to get certain nodes from a treeview followed by getting text from those nodes, filtering text to remove unique and then appending custom image to the duplicate nodes.
For this I am having to loop 4 times. Is there is a simpler way of doing this? I am worried about it's performance for large amount of data.
//Append duplicate item nodes with custom icon
function addRemoveForDuplicateItems() {
var treeView = $('#MyTree').data('t-TreeView li.t-item');
var myNodes = $("span.my-node", treeView);
var myNames = [];
$(myNodes).each(function () {
myNames.push($(this).text());
});
var duplicateItems = getDuplicateItems(myNames);
$(myNodes).each(function () {
if (duplicateItems.indexOf($(this).text()) > -1) {
$(this).parent().append(("<span class='remove'></span>"));
}
});
}
//Get all duplicate items removing unique ones
//Input [1,2,3,3,2,2,4,5,6,7,7,7,7] output [2,3,3,2,2,7,7,7,7]
function getDuplicateItems(myNames) {
var duplicateItems = [], itemOccurance = {};
for (var i = 0; i < myNames.length; i++) {
var dept = myNames[i];
itemOccurance[dept] = itemOccurance[dept] >= 1 ? itemOccurance[dept] + 1 : 1;
}
for (var item in itemOccurance) {
if (itemOccurance[item] > 1)
duplicateItems.push(item);
}
return duplicateItems;
}
If I understand correctly, the whole point here is simply to mark duplicates, right? You ought to be able to do this in two simpler passes:
var seen = {};
var SEEN_ONCE = 1;
var SEEN_DUPE = 2;
// First pass, build object
myNodes.each(function () {
var name = $(this).text();
var seen = seen[name];
seen[name] = seen ? SEEN_DUPE : SEEN_ONCE;
});
// Second pass, append node
myNodes.each(function () {
var name = $(this).text();
if (seen[name] === SEEN_DUPE) {
$(this).parent().append("<span class='remove'></span>");
}
});
If you're actually concerned about performance, note that iterating over DOM elements is much more of a performance concern than iterating over an in-memory array. The $(myNodes).each(...) calls are likely significantly more expensive than iteration over a comparable array of the same length. You can gain some efficiencies from this, by running the second pass over an array and only accessing DOM nodes as necessary:
var names = [];
var seen = {};
var SEEN_ONCE = 1;
var SEEN_DUPE = 2;
// First pass, build object
myNodes.each(function () {
var name = $(this).text();
var seen = seen[name];
names.push(name);
seen[name] = seen ? SEEN_DUPE : SEEN_ONCE;
});
// Second pass, append node only for dupes
names.forEach(function(name, index) {
if (seen[name] === SEEN_DUPE) {
myNodes.eq(index).parent()
.append("<span class='remove'></span>");
}
});
The approach of this code is to go through the list, using the property name to indicate whether the value is in the array. After execution, itemOccurance will have a list of all the names, no duplicates.
var i, dept, itemOccurance = {};
for (i = 0; i < myNames.length; i++) {
dept = myNames[i];
if (typeof itemOccurance[dept] == undefined) {
itemOccurance[dept] = true;
}
}
If you must keep getDuplicateItems() as a separate, generic function, then the first loop (from myNodes to myNames) and last loop (iterate myNodes again to add the span) would be unavoidable. But I am curious. According to your code, duplicateItems can just be a set! This would help simplify the 2 loops inside getDuplicateItems(). #user2182349's answer just needs one modification: add a return, e.g. return Object.keys(itemOccurance).
If you're only concerned with ascertaining duplication and not particularly concerned about the exact number of occurrences then you could consider refactoring your getDuplicateItems() function like so:
function getDuplicateItems(myNames) {
var duplicateItems = [], clonedArray = myNames.concat(), i, dept;
for(i=0;i<clonedArray.length;i+=1){
dept = clonedArray[i];
if(clonedArray.indexOf(dept) !== clonedArray.lastIndexOf(dept)){
if(duplicateItems.indexOf(dept) === -1){
duplicateItems.push(dept);
}
/* Remove duplicate found by lastIndexOf, since we've already established that it's a duplicate */
clonedArray.splice(clonedArray.lastIndexOf(dept), 1);
}
}
return duplicateItems;
}
//get email from test area
var emailList = document.getElementById("emailTextarea").value;
var emailListArray = emailList.split("\n");
//Remove yahoo and duplicates from the list
var usernamesArray = emailListArray.map(function(val, index, arr) {
return val.slice(0, val.indexOf('yahoo.com'));
});
Assuming:
emailListArray = ['123#gmail.com','123#yahoo.com','123#gmail.com','123#hotmail.com']
You can do something like this:
var usernamesArray = [];
emailListArray.forEach(function(item) {
if(usernamesArray.indexOf(item) < 0 && item.indexOf('yahoo.com') < 0) {
usernamesArray.push(item);
}
});
First condition checks if the element in turn is not already in the array of results, second condition checks if the element doesn't contain the substring yahoo.com, if both are true, the element is added to the results.
After that, usernamesArray should have:
[ '123#gmail.com', '123#hotmail.com' ]
I'm working on a web game and need to check for which cells on the table have been selected by the user. Right now I'm just checking for the row and cell index value:
JavaScript
function checkForWin() {
var card = document.getElementById('card');
if ((card.rows[0].cells[0].marker && // 1st row
card.rows[0].cells[1].marker &&
card.rows[0].cells[2].marker &&
card.rows[0].cells[3].marker &&
card.rows[0].cells[4].marker)) {
youWin();
} else {
noWin();
}
}
Is there a more elegant of doing this with jQuery?
Just make some loop :
function checkForWin() {
var card = document.getElementById('card');
var win = true;
for (var i = 0; i < card.rows[0].cells.length; i++){
if(!card.rows[0].cells[i])
win = false;
}
if(win)
youWin();
else
noWin();
}
Using jQuery you could iterate over the list of marked cells or just get the list of marked cells like this:
var marked = $('#cards td.marked');
// If you have a special way to detect a cell is marked that
// needs more custom test than checking the class you can use .filter.
// Just as example I use the same condition.
//var marked = $('#cards td').filter(function () {
// return $(this).hasClass('marked');
//});
// If you want to just iterate the selected cells.
marked.each(function () {
var i = $(this).closest('tr').index();
var j = $(this).index();
console.log(i, j);
});
// If you want to the the array of selected cells.
var indexes = marked.map(function () {
return {
i: $(this).closest('tr').index(),
j: $(this).index()
};
}).get();
To make it easier I assumed that a cell with the marked class means a marked cell. However you can use the condition you want to get the list of marked cells.
See small demo
I am creating a program using JavaScript while a clicking of button it will select a seat and change its background color to green and at the same time the button value will be added to the text field and will toggle accordingly.
Issue: I am adding all the value to the text field using an array, which is successful but during toggling it cannot able to subtract the particular clicking button value from array.
Here I cannot able to use jQuery because this page is coming from a ajax-page load.
// JavaScript Document
var Cur_id;
var Cur_val;
function setId(id, value) {
Cur_id = id;
Cur_val = value;
var SeatVal = document.getElementById(id);
if (SeatVal.style.backgroundImage == "") {
SeatVal.style.backgroundImage = "url(\'themes/frontend/images/greenseat.png\')";
var txbtElementSeat = new Array(document.getElementById("selectedseat").value += Cur_val + ",");
} else if (SeatVal.style.backgroundImage == 'url("themes/frontend/images/greenseat.png")') {
SeatVal.style.backgroundImage = "url(\'themes/frontend/images/seat.png\')";
var txbtElementSeatnbg = document.getElementById("selectedseat").value;
removeSeat(txbtElementSeatnbg, Cur_val);
function removeSeat(txbtElementSeatnbg, value) {
for (var i = 0; i <= txbtElementSeatnbg.length; i++) {
if (txbtElementSeatnbg[i] == value) {
txbtElementSeatnbg.splice(i, 1);
break;
}
}
}
} else if (SeatVal.style.backgroundImage == 'url("themes/frontend/images/seat.png")') {
SeatVal.style.backgroundImage = "url(\'themes/frontend/images/greenseat.png\')";
var txbtElementseatnb = document.getElementById("selectedseat").value += Cur_val + ",";
}
}
Your main issue seems to be that you try to save an array into a textfield. That will actually work, but will convert the array to a string representation.
var a = document.getElementById('selectedseat').value; therefore loads the string representation of the array into a variable "a", not the array itself!
Why don't you use a variable in the outer context (not the local function scope of setId()!) to hold the array? Maybe somewhat like this:
// Create a variable for the array!
var selectedSeats = new Array();
// Build a function that will update the textfield.
// Call this, whenever the array gets changed!
function updateListOfSelectedSeats() {
document.getElementById('selectedseat').value = selectedSeats.join(',');
}
// Removes a seat from the list of selected seats
function removeSeatFromList(seat) {
for (var i = 0; i < selectedSeats.length; i++) {
if (selectedSeats[i] == seat) {
selectedSeats.splice(i, 1);
updateListOfSelectedSeats();
break;
}
}
}
// Now the function that reacts to clicks on a seat
function setId(id, value) {
var Seat = document.getElementById(id);
switch (Seat.style.backgroundImage) {
case 'url("themes/frontend/images/greenseat.png")':
// Seat is already selected and needs to be deselected
Seat.style.backgroundImage = 'url("themes/frontend/images/seat.png")';
removeSeatFromList(value);
break;
case '':
case 'url("themes/frontend/images/seat.png")':
// Seat is empty, select it!
Seat.style.backgroundImage = 'url("themes/frontend/images/greenseat.png")';
selectedSeats.push(value);
updateListOfSelectedSeats();
break;
}
}
To remove the seat from the list use this
//remove seat from list
function removeSeat(seatListElm, seatValue) {
var arr=seatListElm.value.split(',');
var p=arr.indexOf(seatValue);
if(p!=-1){
arr.splice(p, 1);
seatListElm.value=arr.join(',');
}
}
seatListElm would be the element that hold "b5,c7,d5,c2"
seatValue would be something like this "c7"
Working demo code: JSFIDDLE