Javascript Adding - javascript

Ok so I have columns I want added together if there is any information in them. So say I have
Accounts
1
2
3
.
There are 4 account spaces but only 3 accounts.
How do I create java script to add this up.

Live Example
HTML:
<ul>
<li id="accounts">
<p> Accounts </p>
<ul>
<li> 1 </li>
<li> 2 </li>
<li> 3 </li>
<li> . </li>
</ul>
</li>
</ul>
JavaScript:
// Get accounts, ul and set sum to 0
var acc = document.getElementById("accounts"),
ul = acc.getElementsByTagName("ul")[0],
sum = 0;
// Filter out text nodes. Returning an array of <LI> elements
var lis = Array.prototype.filter.call(ul.childNodes, function(li) {
if (li.tagName === "LI") {
return true;
}
});
// Loop through LIs adding up the sum
for (var i = 0, ii = lis.length; i < ii; i++) {
// If it's the last LI element then set the textContent.
if (i === ii - 1) {
lis[i].textContent = sum;
} else {
sum += +lis[i].textContent;
}
}
Disclaimer: Requires Modern Browser or Modernizr.

If your real markup is a list like that, you could do something like this:
// using jquery syntax for brevity; consider it pseudocode
var jList = $('#accounts');
function addColumns() {
var iSum = 0;
jList.find('li').each(function() {
var jLI = $(this);
if(parseFloat(jLI.text()) != 'NaN')
iSum += parseFloat(jLI.text());
});
return iSum;
}
This isn't super-great code. If you give us a little more info about what you're working with, something a little more robust should suggest itself. But the basic idea is that you check a set of elements-of-interest to see if they have summable content (i.e. if their text content can be interpreted as a number); then you add the summable items together.
The requirements of an algorithm like that will impose constraints on the way your "columns" can be marked-up. But there's a near-infinite set of possibilities.

Related

How to target a specific element using Javascript in nested code?

I ran into a problem of targeting different nested levels.
I read that it is possible to use .children feature + for loop, but I failed to do that.
Let's say I want to have a function where you pass the nesting level and it will change some property of <li> element on that level only.
I wrote this function to add classes to all last <li>
function changeElement(){
var lastLi = document.querySelectorAll('li:last-child');
for(i = 0; i < lastLi.length; i++){
lastLi[i].classList.add('last');
lastLi[i].style.background = 'green';
}
}
But now I need to target <li> elements on specific nested level
function changeElementOnLevel(level) {
var lastLi = document.querySelectorAll('li:last-child');
for (i = 0; i < lastLi.length; i++) {
//if level is 1 than we should change ul.root > li:last-child
//if level is 3 than we should change ALL <li:last-child> inside ul.root
> li > ul >
}
}
changeElementOnLevel(3)
<ul class='root'>
<li>Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
I consider that you have some nested ul tags, so in your example, the ul.root is level 1 and the inner ul is level 2 and if instead of 'Eats1' you have a ul tag, it will be level 3 and ...
Use this function:
function changeElementOnLevel(level){
let query = 'ul.root>li';
for (let i = 1; i < level; i++) {
query += '>ul>li'
}
query += ':last-child'
var lastLi = document.querySelectorAll(query);
for(i = 0; i < lastLi.length; i++){
// your code
}
}
It's depends what exactly you want to do. Are you going to change inner HTML on click? What do you mean by saying third level? You have 3 levels, and 3 lists. More information needed in order to help you.
var myElement = document.getElementsByClassName('root');
var arrayOfPets = myElement[0].children; // we are getting first level, all animals
var secondLevelArr = [];
for(i = 0; i < arrayOfPets.length; i++){
arrayOfPets[i].classList.add('last');
arrayOfPets[i].style.background = 'green';
var secondLevel = arrayOfPets[i].children[0].children;
// Push results to array
secondLevelArr.push(secondLevel);
} // will add class and background to all of them
// To add styles only to last element, you do not need to loop through them
arrayOfPets[arrayOfPets.length - 1].style.background = 'red';
for(i = 0; i < secondLevelArr.length; i++){
secondLevelArr[i][0].style.color = "white";
for(j = 0; j < secondLevelArr[i].length; j++){
secondLevelArr[i][j].style.textDecoration = 'line-through';
}
secondLevelArr[i][secondLevelArr[i].length - 1].style.textDecoration = 'none';
}
<ul class='root'>
<li>Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
</ul>
Here's what I came up with on the fly:
First, I'd make a small tweak to your HTML (I'm not sure what you have is actually valid...it might display how you like, but structurally it's going to cause problems). It is going to be difficult to set the value of an "li", if it has the value AND a nested list. If you reset the innerText or the innerHtml, you're going to completely overwrite the rest of the HTML in that tag, meaning you'll lose the nested list. You could work around this, but why bother, just close those tags predictably.
(Note I don't think any of the other answers address this issue).
So I'd first do this, notice how I close the "li" for Cat, Dog, and Fish:
<ul class='root'>
<li>Cat</li>
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
<li>Dog</li>
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
<li>Fish</li>
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</ul>
Now you can select elements and set values very straightforwardly (and the HTML is sound); the selectors here basically say "give me the li and ul children that are ONLY direct descendants of whatever element I'm currently working with" (otherwise you will get all of them no matter how deeply nested, which you don't want).
This code gets you the desired result by working recursively on nested "li" and "ul" collections, note that it also works on the top level "li" collection:
const top_layer = document.querySelectorAll ( '.root' );
const the_new_val = 'THE NEW VAL';
function setProps ( elems, level ) {
Array.from ( elems ).forEach ( x => {
const li = x.querySelectorAll ( ':scope > li' );
const ul = x.querySelectorAll ( ':scope > ul' );
if ( Array.from ( li ).length >= level ) {
li [ level ].innerText = the_new_val;
setProps ( li [ level ].children, level );
}
if ( Array.from ( ul ).length ) {
setProps ( ul, level );
}
});
}
setProps ( top_layer, 2 );
Yes, you could work with "children", but since we are directly interested in setting "li" values, which always appear in "ul" tags, the explicit selectors make it more obvious what's going on and would ignore any other children that may be around, feel free to make that change if you like.
The displayed result:
It is not very clear what you are trying to achieve :) But you can try :nth-child() - CSS pseudo-class selector that allows you to select elements based on their index (source order) inside their container.
This is just an example:
function find(n) {
// returns NodeList
var liNodeList = document.querySelectorAll('li:nth-child(' + n + ')');
console.log(li);
// if you need to do something with those elements, you can iterate
for (var i = 0; i < liNodeList.length; ++i) {
var item = liNodeList[i];
// do what you need with particular item
}
}
Also, the right method is querySelectorAll(...). What you are using querySelectAll does not exist.
Try like below,
I had used mix of query selectors and traversal to achieve this,
function changeElementOnLevel(level) {
var rootElement = document.querySelector(".root");
let targetLi;
if (level === 1) {
targetLi = rootElement.children[rootElement.children.length - 1];
let ul = targetLi.querySelector("ul"); // Since a ul is also there in level 1, I am expecting we need to change only li value not the ul
targetLi.textContent = "changed Value";
targetLi.append(ul);
} else if (level === 3) {
targetLi = rootElement.querySelector(
"li:last-child ul li:last-child"
);
targetLi.textContent = "changed Value";
}
}
changeElementOnLevel(3);
<ul class="root">
<li>
Cat
<ul>
<li>Eats1</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Meows</li>
</ul>
</li>
<li>
Dog
<ul>
<li>Eats2</li>
<li>Sleeps</li>
<li>Snuggles</li>
<li>Plays</li>
<li>Barks</li>
</ul>
</li>
<li>
Fish
<ul>
<li>Eats3</li>
<li>Sleeps</li>
<li>Swims</li>
<li>Plays</li>
<li>Swims</li>
</ul>
</li>
</ul>
This code resolved my issue:
var parent = document.querySelector('.root');
function setFirstItemClass(element, level){
level = +level;
if(level == 1){
console.dir(element);
if(element.children.length > 0){
element.children[0].style.backgroundColor = 'red';
}
} else {
if(element.children.length > 0){
level--;
for(child of element.children){
setFirstItemClass(child, level);
}
}
}
}
setFirstItemClass(parent, 3);

Is there any programmtical way where I can remove class of list item

I am doing one loop where I am matching with some value and assigning class="hidden" to list. But When I run again the loop I want all my list should be without class, so I can assign for other value.
Here is my code.
for (i = list.children.length; i--;) {
li = list.children[i];
match = li.textContent.toLowerCase().indexOf(value.toLowerCase()) > -1;
li.classList.toggle('hidden', !match)
}
But before I run this loop I want all the list without any class so hete in the list I want to remove Class="hidden"
<li class="hidden">
Albania
</li>
Can anyone help me to achieve this
You want to do this before your existing loop? Try this:
var list = document.getElementById("list");
for (i = list.children.length; i--;) {
li = list.children[i];
li.classList.remove("hidden");
}
<ul id="list">
<li class="hidden">foo</li>
<li>bar</li>
<li class="hidden">baz</li>
</ul>
Though it does look like you could do this in the beginning of your existing loop. No need for another loop before that one.
Why not use a combo of Element.classList.contains(), ..remove(), ..add() etc. Lots of info on the MDN page.
For example:
for(i=list.children.length; i--;) {
li = list.children[i];
if (li.textContent.toLowerCase().indexOf(value.toLowerCase()) > -1) {
li.classList.add('hidden');
// or is it li.classList.remove('hidden'); here?
}
}
It is safe to ..add() even if the element has the class already. The class will not be added twice.

Sort through multiple list using jQuery

I have multiple list on a page. An example of a list looks like this:
<ul class="effects-list">
<li data-sorte="2">creative</li>
<li data-sorte="1">euphoric</li>
<li data-sorte="2">uplifted</li>
<li data-sorte="1">energetic</li>
<li data-sorte="0">lazy</li>
<li data-sorte="1">focused</li>
<li data-sorte="2">happy</li>
<li data-sorte="0">talkative</li>
<li data-sorte="0">giggly</li>
<li data-sorte="0">tingly</li>
<li data-sorte="0">hungry</li>
<li data-sorte="0">sleepy</li>
<li data-sorte="0">aroused</li>
</ul>
I have a script that will remove all data-sorte that equals 0. After that is done, it sorts the list from highest to lowest (again by the numbers stored in data-sorte). It then takes the top three options and removes the rest.
Here is the script that does this:
$('*[data-sorte="0"]').remove();
$(".effects-list li").sort(sort_li_effects).appendTo('.effects-list');
function sort_li_effects(a, b){
return ($(a).data('sorte')) < ($(b).data('sorte')) ? 1 : -1;
}
$(".effects-list li").filter( function(k, v) {
if( k < 3 ) {
min = parseInt($(v).data('sorte'));
return false;
} else
return min > parseInt($(v).data('sorte'));
}).remove();
The problem I have is it sorts all of the list based on the first list. My question is how do I modify the script so it sorts all of the list on the page correctly?
Here is a jsFiddle with working code that shows the problem.
EDIT
To clarify a point. Lets say I have the following list:
<ul class="effects-list">
<li data-sorte="2">creative</li>
<li data-sorte="1">euphoric</li>
<li data-sorte="2">uplifted</li>
<li data-sorte="1">energetic</li>
<li data-sorte="0">lazy</li>
<li data-sorte="1">focused</li>
<li data-sorte="1">happy</li>
<li data-sorte="0">talkative</li>
<li data-sorte="0">giggly</li>
<li data-sorte="0">tingly</li>
<li data-sorte="0">hungry</li>
<li data-sorte="0">sleepy</li>
<li data-sorte="0">aroused</li>
</ul>
I would want it to show creative, uplifted, euphoric, energetic, focused and happy as those are the top options by the numbers. euphoric, energetic, focused and happy are all tied at 1 thus I want to show them all. The original script does this.
I slightly modified your script.
$('*[data-sorte="0"]').remove();
$(".effects-list").each(function() {
var $list = $(this),
$items = $list.find('li'),
sortes = [];
$items.detach().sort(sort_li_effects).filter(function(i) {
var sorte = $(this).data('sorte');
if (i < 3) {
sortes.push(sorte);
return true;
}
return sortes.indexOf(sorte) >= 0;
}).appendTo($list);
});
function sort_li_effects(a, b) {
return ($(a).data('sorte')) < ($(b).data('sorte')) ? 1 : -1;
}
http://jsfiddle.net/KK2bV/5/ or http://jsfiddle.net/KK2bV/6/ or http://jsfiddle.net/KK2bV/3/
Major differences:
It goes through li in every list and sorts correctly
It removes all unnecessary items at once using li:gt(2)
You can do this in the following three steps:
$('.effects-list').replaceWith(function() {
// step 1: grab list of items, remove all zero values and sort descending
var $items = $(this).children()
.filter(function() {
return $(this).data('sorte') !== 0;
}).sort(function(a, b) {
var a_value = $(a).data('sorte'),
b_value = $(b).data('sorte');
if (a_value != b_value) {
return a_value > b_value ? -1 : 1;
}
return 0;
}),
current,
levels = 0,
index = 0;
// step 2: work out the top three
while (index < $items.length && levels < 3) {
var value = $items.eq(index).data('sorte');
if (current === null || current != value) {
current = value;
++levels;
}
++index;
}
// step 3: replace the contents
return $items.slice(0, index);
});
See also: .replaceWith()
Demo
I don't like jQuery, however I can provide a javascript solution. I can recommend that you structure your HTML so that each UL has an ID that is enumerable. So:
<ul class="effects-list" id="list1"> ... </ul>
<ul class="...-list" id="list2">...<ul>
And so on. Then use a for loop to go through all the lists like this. Assume that there are 10 lists:
for (var i = 0; i < 10; i++) {
var list = document.getElementById("list"+i);
// Then run your jQuery code on the list 10 times.
}
Does that help?

How to determine DOM position of element in a series of elements when clicked

In this scenario, I have a series of list items each of which has a corresponding content area. When clicking a list item, a corresponding content area is manipulated (i.e., if the first list item is clicked, then the first content section would be manipulated).
<ul>
<li>List item</li>
<li>List item</li>
<li>List item</li>
</ul>
<div>
<section>Content section</section>
<section>Content section</section>
<section>Content section</section>
</div>
My old-school way of doing this was giving each list item and section an id, such as "li1", "li2", etc. and "section1", "section2", etc. I would then parse the integer off the id of the element that was clicked and manipulate the corresponding section.
Is there a way to determine this without needing extra id attributes? E.g., if I click the 3rd list item and know that is the 3rd, I can use document.querySelector('div:nth-child(3)') to manipulate the third content section. My question is how to know it was the 3rd element in a series that was clicked to begin with.
My first-thought solution was something like this:
var target = e.target;
var parent = e.target.parentNode;
for (var i in parent.childNodes) {
if (parent.childNodes[i].nodeType == 1 && parent.childNodes[i] == target) {
// found it... i+1
}
}
This seems like a rather expensive operation compared to just using IDs, especially if there were many more list items and content sections. I'm hoping there is some node attribute that will give me the correct DOM position that I haven't yet found.
Modern browser-only solutions welcomed.
So i have no ide what you are doing here but there must be a more data oriented approach to this.
Like both the li and the section is referring to the same Product or Person or something so you can find it by that reference.
otherwise you can use the previousElementSibling method to count your location like this
var position = function(el) {
var count = 1;
for(var cur = el.previousElementSibling;
cur !== null; cur = cur.previousElementSibling) {
count++;
}
return count;
};
gl
I’d use something like:
var target = e.target,
i = 0;
while(target = target.previousSibling) {
i += target.nodeType == 1;
}
alert('you clicked on '+i);
Fiddle: http://jsfiddle.net/K5Qg9/1/
You can also try using a data lib or assign stuff to the element expano onload:
var elems = document.getElementsByTagName('li'),
i=0;
for(; elems[i]; i++) {
elems[i].rel = i;
}
Then just fetch e.target.rel onclick. Fiddle: http://jsfiddle.net/UnrCt/
If you can use jQuery: $(elem).index()
Update 2016
Well I've run into this issue again nearly 5 years later only this time I used a much simpler solution using a built-in Array method:
var index = Array.prototype.indexOf.call(event.target.parent.children, event.target);

Pure javascript way to update CSS class attribute from all list items?

I'd like to use Javascript (not jquery) to access all items in a <ul> list and remove the active class from everything except my chosen menu item.
Here is the list:
<ul id='flash-menu'>
<li id="menu1" class='something active'>item 1</li>
<li id="menu2" class='somethingelse'>item 2</li>
<li id="menu3" class='somethingelse'>item 3</li>
</ul>
This is my javascript:
function updateMenu(view_name) {
var list_items = document.getElementById('flash-menu').childNodes;
for (var i=0 ; i<list_items.length ; i++){
list_items[i].className = list_items[i].className.replace('/\bactive\b/','');
}
document.getElementById(view_name).className += " active";
}
The last line of the Javascript (adding the active class) works, but I don't think I'm accessing the list items right to remove the classes from the other items. Any suggestions? - thanks!
First off, your regex is wrong:
list_items[i].className.replace(/\bactive\b/, '');
Note: No quotes on regex'es in JavaScript. A slighty altered, working version is available on JsFiddle.
Furthermore, I get a few instances of HTMLTextElements in list_items. They're breaking the loop (Fx3.6/Win7) when trying to access the non-existing className attribute. You can avoid this by either using:
var list_items = document.getElementById('flash-menu').getElementsByTagName('li');
// Selecting _all_ descendant <li> elements
or by checking for the existence of .className before read/write within the loop body (example). The latter is probably the cleanest choice since it still only affects direct children (you may have several levels of <ul>s in each <li>).
I.e.,
function updateMenu(view_name) {
var list_items = document.getElementById('flash-menu').childNodes;
for (var i=0, j=list_items.length; i<j; i++){
var elm = list_items[i];
if (elm.className) {
elm.className = elm.className.replace(/\bactive\b/, '');
}
}
document.getElementById(view_name).className += ' active';
}
You can use javascript function getElementsByTagName:
var listitems = document.getElementsByTagName("li");
this would return an array of all the lists and can be iterated for each list element and processed as required.
You can try:
In the case that you can have more than ul, first you have to get all references to them and then process each ul:
var uls = document.getElementsByTagName("ul");
for (uli=0;uli<uls.length;uli++) {
ul = uls[uli];
if (ul.nodeName == "UL" && ul.className == "classname") {
processUL(ul);
}
}
An illustration of proccessUL can be:
function processUL(ul) {
if (!ul.childNodes || ul.childNodes.length == 0) return;
// Iterate LIs
for (var itemi=0;itemi<ul.childNodes.length;itemi++) {
var item = ul.childNodes[itemi];
if (item.nodeName == "LI") {
// Iterate things in this LI
in the case that you need it put your code here
.....
}
}
}
Of course you can also use: item.className = "classname"; if you dont need to iterate between childs of LI
document.getElementById('flash-menu').childNodes will also include white space nodes.
function updateMenu(view_name) {
var list_items = document.getElementById('flash-menu').getElementsByTagName('li'), i;
for (i=0 ; i<list_items.length ; i++){
if (list_items[i].className.indexOf('active') > -1) {
list_items[i].className = list_items[i].className.replace(/\bactive\b/,'');
}
}
document.getElementById(view_name).className += " active";
}
i agree with jensgram,and you'd better code like this:
list_items[i].className.replace(/\bactive\b/g, '');
add the regex string a 'g'
g is for Global ,using ‘/g’ can replace all the same Which Match the regex ,but if you don't use '/g',you just replace the first string .
like this :
var test= "testeetest" ;
alert(test.replace(/e/,"")) ;//result
: tsteetest but using 'g' var
test= "testeetest" ;
alert(test.replace(/e/g,"")) ;//result
: tsttst
Have a look at this here: https://developer.mozilla.org/en-US/docs/Web/API/element.classList
It helped me a lot with finding class elements!
This is my solution, maybe not the best, but for my works fine.
window.addEventListener('load', iniciaEventos, false);
function iniciaEventos(e)
{
var menu = document.querySelectorAll('nav li');
for(var i = 0; i < menu.length; i++ )
{
menu[i].addEventListener('mousedown', clickMenu);
}
}
function clickMenu()
{
var menu = document.querySelectorAll('nav li');
for(var i = 0; i < menu.length; i++)
menu[i].classList.remove('active');
this.classList.add('active');
}

Categories

Resources