Programmatically push values into an empty object - javascript

Is there a way to push values into an empty object? I have here is a list of countries displaying the country name and population. I want to insert those values into an object much like the example below.
<ul class="countries">
<li class="country">
<span class="country-name">Philippines</span>
<span class="country-population">200</span>
<span class="description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Brunei</span>
<span class="country-population">200</span>
<span class="description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Malaysia</span>
<span class="country-population">400</span>
<span class="country-population">Blablabla</span>
</li>
</ul>
var countries = [
{name: "Philippines", population: 200, description: "Blablabla"},
{name: "Brunei", population: 200, description: "Tatatata"},
{name: "Malaysia", population:4100, description: 'Zzazazaza'}
]
0: {name: "Philippines", population: 200, description: "Blablabla"}
1: {name: "Brunei", population: 200, description: "Tatatata"}
2: {name: "Malaysia", population: 400, description: "Zzazazaza"}
I tried the script below but only inserts the name of the country. How can also insert the population and description values?
var countries_new = [];
$('.countries > .country > .country-name').each(function() {
countries_new.push({
name: $(this).text()
});
});

You can use find() to get the reference of each context inside the loop. Also notice that + which is prefixed to change that text to a number type. You can also use parseInt() there. Using trim() will be helpful to remove extra leading and trailing whitespaces in the objects.
var countries = [];
$('.countries .country').each(function(){
var countryRef = $(this);
countries.push({
name: countryRef.find('.country-name').text().trim(),
population: +(countryRef.find('.country-population').text().trim()),
description: countryRef.find('.description').text().trim()
});
});
console.log(countries);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="countries">
<li class="country">
<span class="country-name">Philippines</span>
<span class="country-population">200</span>
<span class="description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Brunei</span>
<span class="country-population">200</span>
<span class="description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Malaysia</span>
<span class="country-population">400</span>
<span class="country-population">Blablabla</span>
</li>
</ul>

Loop over the .country elements instead, then find the elements within each .country with the other information. As per mock array, population seems to be a number and text() returns string, use Number() to convert it.
var countries_new = [];
$('.countries > .country').each(function() {
countries_new.push({
name: $(this).find(".country-name").text(),
population: Number($(this).find(".country-population").text()),
description: $(this).find(".description").text()
});
});
console.log(countries_new);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="countries">
<li class="country">
<span class="country-name">Philippines</span>
<span class="country-population">200</span>
<span class="description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Brunei</span>
<span class="country-population">200</span>
<span class="description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Malaysia</span>
<span class="country-population">400</span>
<span class="description">Blablabla</span>
</li>
</ul>

You don't need jQuery for this, just select all countries and from that element select the values you want:
console.log(
[...document.querySelectorAll('.countries>.country')].map(
(el) => ({
name: el.querySelector('.country-name').innerText.trim(),
population: el.querySelector('.country-population')
.innerText.trim(),
description: el.querySelector('.description')
.innerText.trim(),
}),
),
);
<ul class="countries">
<li class="country">
<span class="country-name">Philippines</span>
<span class="country-population">200</span>
<span class="description">P Blablabla</span>
</li>
<li class="country">
<span class="country-name">Brunei</span>
<span class="country-population">200</span>
<span class="description">B Blablabla</span>
</li>
<li class="country">
<span class="country-name">Malaysia</span>
<span class="country-population">400</span>
<span class="description">M Blablabla</span>
</li>
</ul>

Assuming all your span elements have CSS classes starting with country- this would be a generic approach:
const countries = document.querySelectorAll('.country');
const result = [];
for (const country of countries) {
let resEl = {};
[...country.querySelectorAll('span[class^="country-"]')].forEach((span) => resEl[span.className.split('-')[1]] = span.innerText)
result.push(resEl);
}
console.log(result)
<ul class="countries">
<li class="country">
<span class="country-name">Philippines</span>
<span class="country-population">200</span>
<span class="country-description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Brunei</span>
<span class="country-population">200</span>
<span class="country-description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Malaysia</span>
<span class="country-population">400</span>
<span class="country-description">Blablabla</span>
</li>
</ul>

You actually not push values into an empty object , you will rather like to create an object and push that object inside an array. So basically you can use array map method which will return an array.
With only javascript you can use document.querySelectorAll which will select all the elements with specified selector( class in this case) .[...] is using spread syntax to convert a live collection to array so that array methods can be use. Here map is an array method. Inside this method create an object and return that
let k = [...document.querySelectorAll('.country')].map(function(curr) {
return {
name: curr.querySelector('.country-name').textContent.trim(),
population: curr.querySelector('.country-population').textContent.trim(),
description: curr.querySelector('.description').textContent.trim()
}
})
console.log(k)
<ul class="countries">
<li class="country">
<span class="country-name">Philippines</span>
<span class="country-population">200</span>
<span class="description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Brunei</span>
<span class="country-population">200</span>
<span class="description">Blablabla</span>
</li>
<li class="country">
<span class="country-name">Malaysia</span>
<span class="country-population">400</span>
<span class="description">Blablabla</span>
</li>
</ul>

Another approach short but effective.
"use strict";
function _toConsumableArray(a) {
if (Array.isArray(a)) {
for (var b = 0, c = Array(a.length); b < a.length; b++) c[b] = a[b];
return c;
}
return Array.from(a);
}
var results = []
.concat(_toConsumableArray(document.querySelectorAll(".countries>.country")))
.map(function(a) {
return {
name: a.querySelector(".country-name").innerText.trim(),
population: a.querySelector(".country-population").innerText.trim(),
description: a.querySelector(".description").innerText.trim()
};
});
console.log(results);

Related

Javascript indexOf a list for a targeted event in a unordered list giving the wrong index

I am trying to get the index number of a 'li' element of a 'ul' from the html.
To do this I change the 'ul' into a list to get his children with:
[...ul.children]
However when I target any of the children I get a -1 as index instead of the correct index.
How to fix?
Is this due to the fact that the list items it's not empty and has a div or other elements inside?
Here is my javascript and my html:
const ul = document.querySelector('ul');
ul.addEventListener('click', myFunc);
function myFunc(e) {
const indexToShow = [...ul.children].indexOf(e.target);
console.log(indexToShow);
console.log(e.target);
}
<ul class="calendar-list">
<li class="list-item">
<div class="fight-link">
<span class="date">1 Dec 2022</span>
<span class="fighters-name">JAY</span>
</div>
</li>
<li class="list-item">
<div class="fight-link">
<span class="date">2 Dec 2022</span>
<span class="fighters-name">Jo</span>
</div>
</li>
<li class="list-item">
<div class="fight-link">
<span class="date">3 Dec 2022</span>
<span class="fighters-name">Bob</span>
</div>
</li>
</ul>
The click evennt triggers on the span, but you're comparing against the li.
So you'll need to search for the li using closest() to match the elements:
const ul = document.querySelector('ul');
ul.addEventListener('click', myFunc);
function myFunc(e) {
const indexToShow = [...ul.children].indexOf(e.target.closest('li'));
console.log(indexToShow);
}
<ul class="calendar-list">
<li class="list-item">
<div class="fight-link">
<span class="date">1 Dec 2022</span>
<span class="fighters-name">JAY</span>
</div>
</li>
<li class="list-item">
<div class="fight-link">
<span class="date">2 Dec 2022</span>
<span class="fighters-name">Jo</span>
</div>
</li>
<li class="list-item">
<div class="fight-link">
<span class="date">3 Dec 2022</span>
<span class="fighters-name">Bob</span>
</div>
</li>
</ul>
here is how you can achive this
const ul = document.querySelector('ul');
ul.addEventListener('click', myFunc);
function myFunc(e) {
const indexToShow = [...ul.children].indexOf(e.path[2]);
console.log(indexToShow);
console.log(e.target);
}

Menu nav li active remove

I want to assign "active" class to the menu. The active clip is throwing but not deleting. I do not understand why you did not delete. Thank you in advance to those who can help.
There is no error, it surprises me that it does not delete this way
<ul id="avia-menu" class="menu av-main-nav">
<li id="menu-item" class="menu-item">
<a href="home">
<span class="avia-bullet"></span>
<span class="avia-menu-text">Home</span>
<span class="avia-menu-fx">
<span class="avia-arrow-wrap">
<span class="avia-arrow"></span>
</span>
</span>
</a>
</li>
<li id="menu-item" class="menu-item">
<a href="about">
<span class="avia-bullet"></span>
<span class="avia-menu-text">About</span>
<span class="avia-menu-fx">
<span class="avia-arrow-wrap">
<span class="avia-arrow"></span>
</span>
</span>
</a>
</li>
<li id="menu-item" class="menu-item">
<a href="contact">
<span class="avia-bullet"></span>
<span class="avia-menu-text">Contact</span>
<span class="avia-menu-fx">
<span class="avia-arrow-wrap">
<span class="avia-arrow"></span>
</span>
</span>
</a>
</li>
</ul>
function updateMenu(url) {
const active = document.querySelector('#menu-item.active');
if (active !== null) {
active.classList.remove('active');
}
const links = Array.from(document.querySelectorAll('#menu-item'));
links.forEach(function (li) {
let anchor = li.querySelector("a");
if (url.indexOf(anchor.href) > -1) {
li.classList.add("active");
}
});
}
updateMenu(window.location.href);
You can simplify your function by consolidating your .add("active") and .remove("active") calls inside your loop. This saves you a query and avoids unsetting and resetting a class on the same element unnecessarily.
You also don't need the Array.from() call.
function updateMenu(url) {
const links = document.querySelectorAll('#menu-item');
links.forEach(li => {
let anchor = li.querySelector("a");
if (url.indexOf(anchor.href) > -1) {
li.classList.add("active");
} else {
li.classList.remove("active");
}
});
}
updateMenu(window.location.href);

Delete button "delete" after clicking in ng-repeat

I'm showing data by ng-repeat. There Have button. to delete item.
After delete item, this button must dissapear only for item that I deleted.
<tr ng-repeat="paymentinfo in paymentList | filter:keyword | filter:money | filter:getdate | filter:{state: 'archived'}: archived.state ? true : false">
<td id="outmouse">
<ul style="list-style: none;" class="gt-reset">
<li class="dropdown changecoursename">
<a class="dropdown-toggle" data-toggle="dropdown">
<span class="tableOperation norlmalstate">Open Course</span>
<span class="tableOperation openedstate">more options</span>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a class="tableOperation" ng-click="paymentRemarks()">Remarks</a></li>
<li><a class="tableOperation" ng-click="paymentReturn(paymentinfo)">Return</a></li>
<li ng-switch on="paymentinfo.state">
<div ng-switch-when="archived" class="archived__state">{{paymentinfo.state}}</div>
<a ng-switch-default class="tableOperation" ng-click="paymentDelete();">{{deletebtn}}</a>
</li>
</ul>
</li>
</ul>
</td>
</tr>
There place where I need to delete
<li ng-switch on="paymentinfo.state">
<div ng-switch-when="archived" class="archived__state">{{paymentinfo.state}}</div>
<a ng-switch-default class="tableOperation" ng-click="paymentDelete();">{{deletebtn}}</a>
</li>
My JS
$scope.datas = [{
date: '06-12-2016',
name: 'Pinao Class',
state: 'archived',
remark: 'remarled',
amount: 101,
id: 21
}, {
date: '15-04-2016',
name: 'drivers Class',
state: 'notarchived',
remark: 'remarled',
amount: 102,
id: 22
}];
$scope.paymentList = $scope.datas;
$scope.deletebtn = "delete";
$scope.paymentDelete = function() {
$scope.www = true;
$scope.deletebtn = false;
}
My code deleting for All elements need delete just for one I choose
try this
ng-click="paymentDelete(this.paymentinfo);">// pass current object to delete function
$scope.paymentDelete = function (paymentinfo ) {
$scope.www = true;
var index = $scope.paymentList.indexOf(paymentinfo );
if (index > -1) {
$scope.paymentList.splice(index, 1);
}
}

Sorting a list by data-attribute

I have a list of people with job titles sorted by the persons’ first names, like this:
<ul>
<li data-azsort="smithjohn">
<a href="#">
<span class="list-name">John Smith</span>
</a>
<span class="list-desc">Professor</span>
</li>
..
<li data-azsort="barnestom">
<a href="#">
<span class="list-name">Tom Barnes</span>
</a>
<span class="list-desc">Lecturer</span>
</li>
</ul>
I’ve added the data-azsort attribute to the <li> element, and I’d like to pop these list elements into an array, and sort based on that data-* attribute (using plain JavaScript).
What would be the best way to sort the list by data-azsort (A-Z), returning the same code? JavaScript only, no jQuery, etc.
This works for any number of lists: it basically gathers all lis in uls that have your attribute, sorts them according to their data-* attribute value and re-appends them to their parent.
Array.from(document.querySelectorAll("ul > li[data-azsort]"))
.sort(({dataset: {azsort: a}}, {dataset: {azsort: b}}) => a.localeCompare(b)) // To reverse it, use `b.localeCompare(a)`.
.forEach((item) => item.parentNode.appendChild(item));
<ul>
<li data-azsort="skeetjon">
<span class="list-name">Jon Skeet</span>
<span class="list-desc">Stack Overflow user</span>
</li>
<li data-azsort="smithjohn">
<span class="list-name">John Smith</span>
<span class="list-desc">Professor</span>
</li>
<li data-azsort="barnestom">
<span class="list-name">Tom Barnes</span>
<span class="list-desc">Lecturer</span>
</li>
</ul>
<ul>
<li data-azsort="smithjohn">
<span class="list-name">John Smith</span>
<span class="list-desc">Professor</span>
</li>
<li data-azsort="barnestom">
<span class="list-name">Tom Barnes</span>
<span class="list-desc">Lecturer</span>
</li>
<li data-azsort="skeetjon">
<span class="list-name">Jon Skeet</span>
<span class="list-desc">Stack Overflow user</span>
</li>
</ul>
The funny thing is, it gets all lis in the same array, sorts them all, but in the end figures out which list the li originally belonged to. It’s a pretty simple and straight-forward solution.
If you want to sort elements by a numeric data attribute, then use this sort function instead:
// Presumably, the data-* attribute won’t be called `azsort`. Let’s call it `numsort`.
({dataset: {numsort: a}}, {dataset: {numsort: b}}) => Number(a) - Number(b) // `Number(b) - Number(a)` to reverse the sort.
A slightly longer ECMAScript 5.1 alternative would be:
Array.prototype.slice.call(document.querySelectorAll("ul > li[data-azsort]")).sort(function(a, b) {
a = a.getAttribute("data-azsort");
b = b.getAttribute("data-azsort");
return a.localeCompare(b);
}).forEach(function(node) {
node.parentNode.appendChild(node);
});
What about getting all of the list items, push them into array which later will be sorted?
var allListElements = document.getElementById("staff").getElementsByTagName("li");
var staff = new Array();
for (i = 0; i < allListElements.length; i++) {
staff.push(allListElements[i].getAttribute('data-azsort'));
}
staff.sort(function(a, b) {
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
//Print
document.write('<h4>Sorted</h4>');
for (i = 0; i < staff.length; i++) {
document.write(staff[i] + "<br />");
}
<h4>Input</h4>
<ul id="staff">
<li data-azsort="smithjohn">
<a href="#">
<span class="list-name">John Smith</span>
</a>
<span class="list-desc">Professor</span>
</li>
<li data-azsort="barnestom">
<a href="#">
<span class="list-name">Tom Barnes</span>
</a>
<span class="list-desc">Lecturer</span>
</li>
</ul>
Additionally you can save the index of <li> and reorder the <ul>.
You can pass a comparison function to Array.prototype.sort
you should be able to do something like
$items = $('li[data-azsort]');
var compareElem = function (a, b) {
if (a.attr('data-azsort') > b.attr('data-azsort') {
return 1;
} else {
return -1
}
};
Array.prototype.sort.apply($items, compareElem);

Changing the onclick of every <li> element in a <ol>

I have a jQueryUI sortable list of li-Buttons with following markup:
<ol id="tracks" class="ui-sortable">
<li class="pl_clipEntry ui-widget-content ui-draggable ui-draggable-handle">
<span class="pl_slot_1_clipNumber_6">6. </span>
<span class="pl_clipName">
<button class="pl_entry" onclick="emitCommand('goto: clip id: 6')">Clip Copy 3.mov</button>
</span>
<span class="pl_clipLength">(00min17sec)</span>
</li>
</ol>
These Items are dragged from another list, where their onclick has its good use
Now everytime the list is sorted (or a new item is inserted), i want to overwrite the onclick, so that the items in THIS list have a different function (in fact, it's going to be a playlist, that's why the onclick-command can't be the same as in the file-list)
i can't figure out how exactly to do this.
This is called on every list-update:
function updatePlaylist () {
var list = $("#tracks");
list.children(':not(.placeholder)').each(function(index) {
console.log($(this).childNodes[1].text()); // to check if it works
//$(this).children[1].attr("onclick","play_clip:" + index);
});
};
so, in plain: i want to set the onclick of each li > span > button to "play_clip6" etc.
i've tried it with children[1] and childnodes[1], but they all print undefined.
i suppose it's something about the scope of "this", or some other thing i've overlooked.. but shouldn't this work? i'm stuck :(
thanks
I think what you are missing is the parameter for the referenced element in the each function.
HTML
<ol id="tracks">
<li>Track 1 <span style="color:red;">sp1</span> <span style="color:blue;">Play 1</span>
</li>
<li>Track 2 <span style="color:red;">sp1</span> <span style="color:blue;">Play 2</span>
</li>
<li>Track 3 <span style="color:red;">sp1</span> <span style="color:blue;">Play 3</span>
</li>
<li>Track 4 <span style="color:red;">sp1</span> <span style="color:blue;">Play 4</span>
</li>
<li class="placeholder">Move Track Here</li>
</ol>
<button id="changeIt">Change The Click Event</button>
JS
$(function () {
$("#tracks").children(":not(.placeholder)").each(function (index, elem) {
var cElem = $(elem).children().eq(1);
$(cElem).click(function () {
alert("before: " + $(cElem).text());
});
});
});
$("#changeIt").click(function () {
$("#tracks").children(":not(.placeholder)").each(function (index, elem) {
var cElem = $(elem).children().eq(1);
$(cElem).attr('onclick', '').unbind('click');
$(cElem).click(function () {
alert("after: " + $(cElem).text());
});
});
});
Edit for selecting button inside
HTML
<ol id="tracks">
<li>Track 1 <span style="color:red;">sp1</span> <span style="color:blue;"><button>Play 1</button></span>
</li>
<li>Track 2 <span style="color:red;">sp1</span> <span style="color:blue;"><button>Play 2</button></span>
</li>
<li>Track 3 <span style="color:red;">sp1</span> <span style="color:blue;"><button>Play 3</button></span>
</li>
<li>Track 4 <span style="color:red;">sp1</span> <span style="color:blue;"><button>Play 4</button></span>
</li>
<li class="placeholder">Move Track Here</li>
</ol>
<button id="changeIt">Change The Click Event</button>
JS
$(function () {
$("#tracks").children(":not(.placeholder)").each(function (index, elem) {
var cElem = $(elem).children().eq(1).children().eq(0);
$(cElem).click(function () {
alert("before: " + $(cElem).text());
});
});
});
$("#changeIt").click(function () {
$("#tracks").children(":not(.placeholder)").each(function (index, elem) {
var cElem = $(elem).children().eq(1).children().eq(0);
$(cElem).attr('onclick', '').unbind('click');
$(cElem).click(function () {
alert("after: " + $(cElem).text());
});
});
});

Categories