I have a few spans:
<span class="first" data-id="1" />
<span class="second" data-id="4" />
<span class="second" data-id="2" />
<span class="third" data-id="5" />
And operations on them:
const spans = document.querySelectorAll('span');
const list = [];
spans.forEach(function(span) {
if (typeof list[span.getAttribute('class')] === 'undefined') {
list[span.getAttribute('class')] = [];
}
list[span.getAttribute('class')].push(span.getAttribute('data-id'));
});
console.log(list);
console.log(JSON.stringify(list));
But JSON.stringify return empty array.
How can I count the number of occurrences of data-id at a given SPAN the easiest way and next get it to string? I would like to send this data to API.
Fiddle: https://jsfiddle.net/x7thc59v/
here is a code that's working:
use object instead of array to have key.
use var instead of const to modify your variable;
const spans = document.querySelectorAll('span');
var list = {};
spans.forEach(function(span) {
if (typeof list[span.getAttribute('class')] === 'undefined') {
list[span.getAttribute('class')] = [];
}
list[span.getAttribute('class')].push(span.getAttribute('data-id'));
});
console.log(list);
console.log(JSON.stringify(list));
If you want output like this [{"first":["1"]},{"second":["4","2"]},{"third":["5"]}]
Then you can follow this appraoch
const spans = document.querySelectorAll('span');
const list = [];
spans.forEach(function(span) {
const className = span.getAttribute('class');
const valIndex = list.findIndex(val => val[className]);
const hasVal = valIndex !== -1;
if (className && hasVal) {
const preVal = list[valIndex][className];
list[valIndex][className] = preVal.concat(span.getAttribute('data-id'));
} else if (className && !hasVal){
list.push({[className]: [span.getAttribute('data-id')]});
}
});
console.log(list);
console.log(JSON.stringify(list));
Here is working jsfiddle;
The JSON.stringify function will not serialise string keys added to an array, only the numeric ones.
The trivial fix then is to replace const list = [] with const list = {} and then update the code that uses the results to expect an Object instead of an Array.
More generally, you should reduce the repetition in your code, especially the repeated calls to span.getAttribute('class'):
const spans = document.querySelectorAll('span');
const list = {};
spans.forEach(function(span) {
const cls = span.className;
const id = span.getAttribute('data-id');
list[cls] = list[cls] || []; // ensure the array exists
list[cls].push(id);
});
I think you wanted the list to be an object as the way you were trying to access the property of list by the class name.
Also rather than mutating an external object using forEach its better to use Array.prototype.reduce on the NodeList returned from document.querySelectorAll() call:
const spans = document.querySelectorAll('span');
//With Array.prototype.reduce
const list = Array.prototype.reduce.call(spans, function(acc, span) {
const attr = span.getAttribute('class');
const dataId = span.getAttribute('data-id');
acc[attr] ? acc[attr].push(dataId) : (acc[attr] = [dataId]);
return acc;
}, {});
console.log(list);
console.log(JSON.stringify(list));
<span class="first" data-id="1" />
<span class="second" data-id="4" />
<span class="second" data-id="2" />
<span class="third" data-id="5" />
Related
I'm just learning javascript and I'm trying to build a To-Do-List app. Using the render function, I render the myList values to the screen after pushing the input values into the myList array.
My problem is creating a deleteButton function that deletes a specific item in the array. A delete button appears right next to entered values on the screen, and when the user clicks on it, that specific item in the array should be deleted. Any advice on how to solve this?
let myList = []
const submitBtn = document.getElementById("submit-btn");
const clearListBtn = document.getElementById("clearList-btn");
const inputEl = document.getElementById("input-btn");
const olEl = document.getElementById("ol-el");
const doneBtn = document.getElementById("done-btn");
function render(leads) {
let listItems = " ";
for ( let i = 0; i < leads.length; i++) {
listItems +=
`<li id = " ">
${leads[i]} <button id= "done-btn" onclick = "deleteButton(${leads[i]})">X</button>
</li>
`;
}
olEl.innerHTML = listItems;
}
submitBtn.addEventListener("click", function() {
myList.push(inputEl.value);
inputEl.value = " ";
render(myList);
})
clearListBtn.addEventListener("click", function() {
myList = [];
render(myList)
})
function deleteButton(value) {
myList.remove(value);
render(myList);
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="box">
<p>To-Do List</p>
<input value = "Add an item!" id = "input-btn">
<button id = "submit-btn">submit</button>
<button id = "clearList-btn">clear list</button>
<ol id="ol-el"></ol>
<script src = "index.js"></script>
</div>
</body>
</html>
use Array.splice with the selected item's index, you won't need to pass in the whole item.
so the on click call would be:
onclick = "deleteButton(${i})"
and the function:
function deleteButton(index) {
myList.splice(index, 1); // deletes one item starting from 'index'
render(myList);
}
for more details on Array.splice - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
This is how I would do it:
Loop through your array and get the index of the item you need to remove using the indexOf method.
Pass the index into the splice method thereby removing that element from the array
Like this:
// Loop through your array
toDoList.forEach(item => {
// Listen for a click on your button element
button.addEventListener('click', () => {
let index = toDoList.indexOf(item) // This tells you the position of the item you want to remove
if (index > -1) { // Make sure the array is not empty
toDoList.splice(index, 1); // This removes the item from the array
}
})
}
Hope this was somehow helpful :)
The function below accepts an id to compare all objects in the array against. NOTE: This method will create a new array that you would have to assign to your variable which I marked with setSortedData()
This functions by utilizing the built in .filter() method for arrays. Essentially I filter out all elements that are the given element by saying keep every element that is not the id.
const deleteItem = (id) => {
const arr = sortedData.filter((item) => item.id !== id);
setSortedData(arr);
};
To remove an element from an existing array use .splice().
How to get key "employee "
{
employee":{"name":"John","age":12}
}
It's not about Angular, this is plain Javascript:
const obj = { employee:{name:"John",age:12}}
const key = Object.keys(obj)[0]
You can use forEach to get your output if you use Array Object
function example1(array){
let res = [];
array = [{"ex1":{"name":"John1","age":12}}, {"ex2":{"name":"John2","age":12}}, {"ex3":{"name":"John3","age":12}}]
array.forEach((e, idx) => {
res.push(Object.keys(e))
})
document.getElementById("ex1").innerHTML = JSON.stringify(res);
}
<input type="button" value="example1" onclick="example1()">
<p id="ex1"></p>
if you just use only object you can use For...in to get your output
function example2(array){
let res = [];
obj = {"ex1":{"name":"John1","age":12}, "ex2":{"name":"John2","age":12}, "ex3":{"name":"John3","age":12}}
for(var val in obj){
res.push(val)
}
document.getElementById("ex2").innerHTML = JSON.stringify(res);
}
<input type="button" value="example2" onclick="example2()">
<p id="ex2"></p>
But remember when you put object or array or whatever check its valid or not. In your object is invalid here is the correct format
{"employee":{"name":"John","age":12}}
There is a React component -
'function Product (props) {
const {
prod_id: id,
prod_name : title,
prod_status: status,
prod_price: price,
prod_oldprice : oldPrice,
} = props;
let oldPriceChecker = (oldPriceValue) => {
if (oldPriceValue) {
let oldPriceStr = oldPriceValue + ' zł';
return(oldPriceStr);
}else {
return('');
}
}
let statusChecker = (value) => {
if (value != undefined){
let string = value;
let array = string.split(',');
console.log(array);
array.map(n => <div className="status">{n}</div>)
}
}
return (
<div className="row">
<div className="card">
<div className="card-image">
<img src="https://via.placeholder.com/150" />
</div>
<div className="card-content">
<span className="card-title">{title}</span>
<hr className="card_hr" />
<p className="card_price" >{price} zł</p>
<div className="card_price old_price">{oldPriceChecker(oldPrice)}</div>
{statusChecker(status)}
</div>
</div>
)
}
export {Product}
Question: The variable prod_status: status can contain several values (for example, "new,promotion", if so, you need to split it into two words and create a separate block for each, since now the block comes with the whole line
It is necessary like this (new, saleout, etc. in separate blocks)
I tried to create a function but it just outputs an array to the console
I think I'm not using the property "map" correctly
The problem:
The function you have created statusChecker does not return anything. Therefore when you want to print it ({statusChecker(status)}) it doesn't do anything.
let statusChecker = (value) => {
if (value != undefined){
let string = value;
let array = string.split(',');
console.log(array);
//This is what you want to return but is not returned
array.map(n => <div className="status">{n}</div>)
}
}
Solution:
return the mapped array from the function.
let statusChecker = (value) => {
if (value != undefined){
let string = value;
let array = string.split(',');
console.log(array);
//This is what you want to return but is not returned
return array.map(n => <div className="status">{n}</div>)
}
}
The main problem with your code is that you are trying to create an html element just by writing it, and that is not possible. The closest thing to that is for you to use innerHTML. for example: parent.innerHTML = <div className="status">${n}</div>. being "parent" an html element previously created (document.createEement()) or already existing in the DOM.
In my solution I used document.createElement() to create a div and insert the text into it. The function returns an array with the div elements and their respective words inside but only that, it doesn't print anything on the screen. If all you want is to display it on the screen the process is a bit different.
let statusChecker = (value) => {
// Instead of doing " if (value != undefined) I used
let nodes = value?.split(',').map(n => {
// first you have to create an element
let div = document.createElement('div')
// add the class status
div.classList.add('status')
// insert each word into the div
div.textContent = n;
return div
})
return nodes
}
console.log(statusChecker('new,promotion'))
Actually, I need two arrays, from two attributes, "id_play" and "orden_play".
I share my code here:
let id_play = [];
let orden_play = [];
id_play = $("#listaOrdenable").children().attr('id_play');
orden_play = $("#listaOrdenable").children().attr('orden_play');
<ul id="listaOrdenable" class="s1">
<li orden_play="0" id_play="47"><img src="uploads/meade.jpg" width="40" heigth="40">1</li>
<li orden_play="1" id_play="49"><img src="uploads/videoIcon.png" width="40" heigth="40">2</li>
<li orden_play="2" id_play="50"><img src="uploads/RARA.jpg" width="40" heigth="40">3</li>
<li orden_play="3" id_play="51"><img src="uploads/videoIcon.png" width="40" heigth="40">4</li>
</ul>
let id_play = [];
let orden_play = [];
const children = $("#listaOrdenable").children().map((childId, child) => child.attributes);
const getAttrFromChild = (attr) => children.get().map(child => child[attr].value);
id_play = getAttrFromChild("id_play")
orden_play = getAttrFromChild("orden_play")
console.log(id_play, orden_play)
Here my solution:
id_play = []
orden_play = []
$("#listaOrdenable").children('li').each(function(){
id_play.push(this.getAttribute('id_play'));
id_play.push(this.getAttribute('orden_play'));
});
I'm using jQuerys $.each to access each attribute in the list. I've added 2 loops at the end of the code for verification. You can get rid of them if you don't need it
let id_play = [];
let orden_play =[];
$.each($('#listaOrdenable').children(), function(i,v){
id_play.push($(this).attr('id_play'));
orden_play.push($(this).attr('orden_play'));
});
//You can remove these two loops if you don't need them
for(var i in id_play) {
console.log(id_play[i]);
}
for(var x in orden_play) {
console.log(orden_play[x]);
}
I have a plain javascript solution:
const arr1 = [].map.call(document.getElementById('listaOrdenable').querySelectorAll('li'), li => li.getAttribute('orden_play'))
const arr2 = [].map.call(document.getElementById('listaOrdenable').querySelectorAll('li'), li => li.getAttribute('id_play'))
What I am basically doing is selecting all the li nodes from the ul and I map over them and grab the desired attribute. The purpose of [].map.call is that querySelectorAll() returns a NodeList which is an Array like structure. It doesn't have all the array functions, so the .call approach I used is a way of using array functions on array like structures.
I haven't tested it in jQuery, but I presume that the .children() function will return what querySelectorAll() returns in my example.
Hope this helps, cheers!
You can use JQuery each on all the children of element listaOrdenable, and then push the attributes you are looking for on the related arrays. Check the next example:
$(document).ready(function()
{
var id_play = [];
var orden_play = [];
$("#listaOrdenable").children().each(function()
{
id_play.push($(this).attr("id_play"));
orden_play.push($(this).attr("orden_play"));
});
console.log(id_play);
console.log(orden_play);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="listaOrdenable" class="s1">
<li orden_play="0" id_play="47"><img src="uploads/meade.jpg" width="40" heigth="40">1</li>
<li orden_play="1" id_play="49"><img src="uploads/videoIcon.png" width="40" heigth="40">2</li>
<li orden_play="2" id_play="50"><img src="uploads/RARA.jpg" width="40" heigth="40">3</li>
<li orden_play="3" id_play="51"><img src="uploads/videoIcon.png" width="40" heigth="40">4</li>
</ul>
In the case, you want to save the elements as integer numbers, you have to use the next code:
id_play.push(Number($(this).attr("id_play")));
orden_play.push(Number($(this).attr("orden_play")));
id_play = []
orden_play = []
$("#listaOrdenable li").each(function() {
id_play.push($(this).attr('id_play'));
id_play.push($(this).attr('orden_play'));
});
The simplest possible pure JS solution:
let id_play = [].slice.call(document.querySelectorAll("li[id_play]"));
let orden_play = [].slice.call(document.querySelectorAll("li[orden_play]"));
This approach is using David Walsh's proposal to convert NodeList to Array.
You can also use Array.from(). See this link.
But, I have to add one more. This is by far the most elegant way, using ES6 spread operator.
ES6 solution:
let id_play = [...(document.querySelectorAll("li[id_play]"))];
let orden_play = [...(document.querySelectorAll("li[orden_play]"))];
How can you create an array with from a set of elements with same rel?
Eg:
<a rel='array' id='2' url='aa'></a>
<a rel='array' id='5' url='bb'></a>
<a rel='array' id='8' url='cc'></a>
Array:
[2] > aa
[5] > bb
[8] > cc
I put each URL as value just to use something. But having the IDs ordered should be enough.
How can this be done?
const anchors = document.getElementsByTagName('a'), arr = [];
for (let i = 0; i < anchors.length; i++){
let current = anchors[i];
if(current.getAttribute('rel') == 'array') {
// arr.push(current.getAttribute('url'));
arr.push({ 'id' : current.id, 'url' : current.getAttribute('url') });
}
}
http://jsfiddle.net/8ScSH/
Or, more succinctly:
const anchors = [...document.getElementsByTagName('a')];
const arrs = anchors.filter(x => x.getAttribute('rel') === 'array')
.map(x => { return { 'id': x.id, 'url': x.getAttribute('url') } });
Not enough jquery!
var arr = [];
$('a[rel="array"]').each(function(){
arr.push($(this).attr('url'));
});
Very easy if you're using jquery:
var arr = [];
$('a[rel="array"]').each(function() {
arr.push($(this).attr('url'));
});
Fiddle
var itemArray = [];
$("a[rel='array']").each(function() { itemArray.push($(this).attr("url") });
Try
var a = {};
$('#wrap a').each(function() {
if($(this).attr('rel') === "array") {
a[$(this).attr('id')] = $(this).attr('url');
}
});
An array won't cut it, you need an object. Here live: http://jsfiddle.net/martincanaval/rAPkL/
var elements = [];
$('a[rel="array"]').each(function() {
elements[this.id] = $(this).attr('url');
});
Note that this creates a sparse array with only the index values specified by the element ids, which doesn't make much sense to me, but that's what you asked for. It really doesn't make sense if any of the ids can be non-numeric - if so you should use an object rather than an array, i.e.:
var elements = {};
"I put each URL as value just to use something. But having the IDs ordered should be enough."
If your intention is to get an array of the ids then you can do this:
var elementIds = [];
$('a[rel="array"]').each(function() {
elementIds.push(this.id);
});
That would create the array ['2', '5', '8'] where the ids will be in the order that they appear within your source html.
However, just saying $('a[rel="array"]') gives you a jQuery object that is an array-like structure holding all the matching elements - that's why we can iterate over each of the elements using the .each() method. So it's possible that whatever you're really trying to do can be done directly with jQuery rather than setting up your own array.