Getting Random Nulls on .querySelector() in for Loop - javascript

<div id="agg-filter-buttons">
<button class="btn filter-btn" onclick="filterSelection(event)"><span data-
value="freespins">Free Spins <div class="num-brands"></div> </span></button>
<button class="btn filter-btn" onclick="filterSelection(event)"> <span data-value="bigbonus">Big Bonus <div class="num-brands"></div> </span></button> </div>
enter code here
</div>
<div class="brand-row the-table-row all row-1 filterDiv bigbonus newPlay show">
Row 1
</div>
<div class="brand-row the-table-row all row-2 filterDiv freespins newPlay show">
Row 2
</div>
filterSelection();
function filterSelection(e) {
var x, i, c;
x = document.getElementsByClassName("filterDiv");
var allBtns = document.getElementsByClassName("filter-btn")
for(i = 0; i<allBtns.length; i++){
let rowsAffected = allBtns[i].querySelector('.num-brands');
rowsAffected.innerText = '';
}
c = "all";
if(e && e.target.dataset){
c = e.target.dataset.value ? e.target.dataset.value : "all";
}
const numBrands = [];
for (i = 0; i < x.length; i++) {
x[i].classList.remove('show');
void x[i].offsetWidth;
if (x[i].classList.contains(c)) {
x[i].classList.add('show');
numBrands.push(i);
}
}
if(e && e.target){
e.srcElement.children[0].innerText = ` (${numBrands.length})`;
}
}
// Add active class to the current control button (highlight it)
var btnContainer = document.getElementById("agg-filter-buttons");
var btns = btnContainer.getElementsByClassName("filter-btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function($this) {
var current = document.querySelectorAll(".active-btn");
current[0].className = current[0].className.replace(" active-btn", "");
this.className += " active-btn";
});
}
var filter = document.getElementById('agg-filter-buttons');
filter.addEventListener('click', function(){
document.getElementsByClassName('section--btable')[0].classList.add('flash');
});
filterSelection();
function filterSelection(e) {
var x, i, c;
x = document.getElementsByClassName("filterDiv");
var allBtns = document.getElementsByClassName("filter-btn")
for (i = 0; i < allBtns.length; i++) {
let rowsAffected = allBtns[i].querySelector('.num-brands');
rowsAffected.innerText = '';
}
c = "all";
if (e && e.target.dataset) {
c = e.target.dataset.value ? e.target.dataset.value : "all";
}
const numBrands = [];
for (i = 0; i < x.length; i++) {
x[i].classList.remove('show');
void x[i].offsetWidth;
if (x[i].classList.contains(c)) {
x[i].classList.add('show');
numBrands.push(i);
}
}
if (e && e.target) {
e.srcElement.children[0].innerText = ` (${numBrands.length})`;
}
}
// Add active class to the current control button (highlight it)
var btnContainer = document.getElementById("agg-filter-buttons");
var btns = btnContainer.getElementsByClassName("filter-btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function($this) {
var current = document.querySelectorAll(".active-btn");
current[0].className = current[0].className.replace(" active-btn", "");
this.className += " active-btn";
});
}
var filter = document.getElementById('agg-filter-buttons');
filter.addEventListener('click', function() {
document.getElementsByClassName('section--btable')[0].classList.add('flash');
});
<div id="agg-filter-buttons">
<button class="btn filter-btn" onclick="filterSelection(event)"><span data-
value="freespins">Free Spins <div class="num-brands"></div> </span></button>
<button class="btn filter-btn" onclick="filterSelection(event)"> <span data-value="bigbonus">Big Bonus <div class="num-brands"></div> </span></button> </div>
enter code here
</div>
>
<div class="brand-row the-table-row all row-1 filterDiv bigbonus newPlay show">
Row 1
</div>
<div class="brand-row the-table-row all row-2 filterDiv freespins newPlay show">
Row 2
</div>
So I have a table with rows. These are filtered with the filter buttons. When you click a filter button it shows only the rows with the same name in its class. This works and populates the button with how many rows are affected.
However this breaks occasionally and I have no idea why.
the Variable rowsAffected works until it becomes null for some unknown reason.
My guess is that the dom eventually doesnt load fast enough for the querySelector to be able to read it. But im not sure.. Any advice very welcome!
I have also added a fiddle
https://jsfiddle.net/8z56sxby/

Since you only care about the first .active-btn (because there is at most one), you can just use querySelector.
Make sure there is a current before you do anything with its classes.
Use element.classList.add and element.classList.remove to add or remove classes.
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
var current = document.querySelector(".active-btn");
if (current) current.classList.remove("active-btn");
this.classList.add("active-btn");
});
}

Thankyou
The answer to this question was that clicks on my button were sometimes clicking child elements which were not setup to handle the click.
A silly mistake - that someone may find useful in the future.

Related

How to select an element from NodeList in JavaScript?

I wanted to make a food order app, and try to make the quantity buttons like this with my own code: https://css-tricks.com/examples/InputNumberIncrementer/
I struggled with the results. There is an input which will set the quantity once incButtons or decButtons is clicked.
But when I click a button, all the inputs change. How do I fix it, if I want the input to change only on the clicked button, not set to all inputs?
this is my source code :
let incButton = document.querySelectorAll('.inc');
let decButton = document.querySelectorAll('.dec');
// Increment Button
for (let i = 0; i < incButton.length; i++){
incButton[i].addEventListener('click', function() {
// show the decButton when quantity > 1
decButton[i].style.display = 'inline';
// increment the quantity when incButton clicked
for (let i = 0; i < incButton.length; i++){
let inputField = document.querySelectorAll('.quantityNumber');
if(incButton) {
let newVal = parseInt(inputField[i].value) + 1;
inputField[i].value = newVal;
console.log('Quantity +1 added');
}
}
});
};
// Decrement Button
for (let i = 0; i < decButton.length; i++) {
decButton[i].addEventListener('click', function () {
let inputField = document.querySelectorAll('.quantityNumber');
for (let i = 0; i < inputField.length; i++) {
// if decButton is clicked and quantitiy >= 2 decrement it
if (decButton[i] && inputField[i].value >= 2) {
let newVal = parseInt(inputField[i].value) - 1;
inputField[i].value = newVal;
console.log('Quantity -1 reduced');
}
// hide the decButton when quantity is 1
if (inputField[i].value == 1) {
decButton[i].style.display = 'none';
}
}
});
};
<div class="quantity">
<button class="dec">-</button>
<input type="text" class="quantityNumber" value="1">
<button class="inc">+</button>
</div>
<br>
<div class="quantity">
<button class="dec">-</button>
<input type="text" class="quantityNumber" value="1">
<button class="inc">+</button>
</div>
Please help me to solve this, and thank you before for the questions :)

I want a paragraph to get the line through style whenever a checkbox is checked and vise versa

It doesn't work as expected
I managed to make it work in this way :
<div class="noteContainer">
<p class="title">Hello World</p>
<p class="note">This is the hello world note!
<i class="fa fa-pencil-square-o noteEdit" title="Edit" aria-hidden="true"></i>
<input onclick="setLineThrough()" type="checkbox" name="done" class="cbDone">
</p>
</div>
let checkBoxes = document.getElementsByClassName("cbDone");
const setLineThrough = () => {
for (let i = 0; i < checkBoxes.length; i++) {
if (checkBoxes[i].checked) {
checkBoxes[i].parentElement.style.textDecorationLine = "line-through";
} else {
checkBoxes[i].parentElement.style.textDecorationLine = "none";
}
}
};
It wasn't exactly what I wanted
But then the line through also goes through my icon and to the checkbox so I decided to get them out of the <p> and made some changes and I was expecting it to work since they were so similar but it just adds the line through but it does not remove the line through when the checkbox is unchecked!
<div class="noteContainer">
<p class="title">Hello World</p>
<p class="note">This is the hello world note!</p>
<i class="fa fa-pencil-square-o noteEdit" title="Edit" aria-hidden="true"></i>
<input onclick="setLineThrough()" type="checkbox" name="done" class="cbDone">
</div>
let checkBoxes = document.getElementsByClassName("cbDone");
let notePara = document.getElementsByClassName("note");
const setLineThrough = () => {
for (let i = 0; i < checkBoxes.length; i++) {
if (checkBoxes[i].checked) {
notePara[i].style.textDecorationLine = "line-through";
} else {
notePara[i].parentElement.style.textDecorationLine = "none";
}
}
};
What is wrong with my second iteration? I just can't find it.
Also take note that there are some css in my code but I don't think it is needed here.
You just need to delete one instance of .parentElement from your JavaScript.
Your JavaScript will then look like this:
let checkBoxes = document.getElementsByClassName("cbDone");
let notePara = document.getElementsByClassName("note");
const setLineThrough = () => {
for (let i = 0; i < checkBoxes.length; i++) {
if (checkBoxes[i].checked) {
notePara[i].style.textDecorationLine = "line-through";
} else {
notePara[i].style.textDecorationLine = "none";
}
}
};

How to add ID to <a href> on click using JavaScript

I have an HTML like this
How add ID to HTML href with javascript
<div class="tab">
exp
</div>
<div class="tab">
exp
</div>
<div class="tab">
exp
</div>
<script>
var els = document.getElementsByClassName("tab");
// loops els
for(var i = 0, x = els.length; i < x; i++) {
els[i].onclick = function(){
x = document.querySelector(".tab> a")
// do something
x.id = "expid";
}
}
</script>
I want to add the id to each tag when I click this. Pls help me. Thks so much
<div class="tabs">
... your html code.
</div>
const tabs = document.querySelector('.tabs')
tabs.addEventListener('click', event => {
const aTag = event.target
if (aTag.tagName !== 'A') return
aTag.id = `EXPID#${getIndexIn(aTag, tabs)}`
})
function getIndexIn(element, parent): number
What's the meaning to be it?
Your call to document.querySelector() always returns the first .tab > a link in the document. You can this.querySelector() to return the link in the DIV that you clicked on instead.
I've changed the code to use a class rather than ID, since you shouldn't have duplicate IDs.
Loop through all the DIVs. If it's the DIV that the user clicked on, add the class, otherwise remove it.
var els = document.getElementsByClassName("tab");
// loops els
for (var i = 0, x = els.length; i < x; i++) {
els[i].onclick = function() {
for (var j = 0; j < els.length; j++) {
x = els[j].querySelector("a");
if (els[j] == this) {
x.classList.add("expid");
} else {
x.classList.remove("expid");
}
}
}
}
.expid {
background-color: yellow;
}
<div class="tab">
exp
</div>
<div class="tab">
exp
</div>
<div class="tab">
exp
</div>
const anchors = document.getElementsByTagName('a');
const ids= [1,2,3,4,5];
let index =0 ;
for(let a of anchors ){
a.href=ids[index++]
}
you can try this way it's a cool and easiest what I do with pure js

What to do when looping repeats in 2 different areas

The code gets the values of the input and sends it to the textarea, but when you add more than one title the values are repeated in the result of the titles, for example, the DESCRIPTIONS of title 1 are the same as in title 2, why does this happen? and how to make it work without changing the purpose?
Run the code in codepen.io or jsfiddle.net
This is what happens:
This is what should happen:
function result() {
var inp2 = document.getElementsByName("inp2");
var titu = document.getElementsByName("titu");
var res = document.getElementById("result");
res.value = "";
if (titu[0]) {
for (var k = 0; k < titu.length; k++) {
if (titu[k].value.trim() != '') {
res.value += `<div>
<span>${titu[k].value.trim()}</span>
</div>
<ul>\n`;
for (var j = 0; j < inp2.length; j++) {
if (inp2[j].value.trim() != '') {
res.value += `<li>${inp2[j].value.trim()}</li>\n`;
}
}
}
}
}else {
console.log("test")
res.value += `<ul>\n`;
for (var l = 0; l < inp2.length; l++) {
if (inp2[l].value.trim() != '') {
res.value += `<li>${inp2[l].value.trim()}</li>\n`;
}
}
}
};
// -----------------------------------------
if (document.getElementById("add2")) {
let cont2 = 1;
document.getElementById("add2").onclick = function clone2() {
let container2 = document.getElementById("output2");
let tempLinha2 = document.querySelector('#template2');
let clone2 = document.importNode(tempLinha2.content, true);
const label2 = clone2.querySelector("label");
label2.htmlFor = cont2;
clone2.querySelector("input").className = cont2;
container2.appendChild(clone2);
cont2++;
};
document.getElementById("del2").onclick = function del2() {
document.querySelector('#output2 #linha2:last-child').remove();
};
}
// ---------------------------------------
if (document.getElementById("addtit")) {
let cont3 = 1;
document.getElementById("addtit").onclick = function clone3() {
let container3 = document.getElementById("output2");
let tempLinha3 = document.querySelector('#template3');
let clone3 = document.importNode(tempLinha3.content, true);
const label3 = clone3.querySelector("label");
label3.htmlFor = cont3;
clone3.querySelector("input").className = cont3;
container3.appendChild(clone3);
cont3++;
document.getElementById('add2').id = 'add3';
document.getElementById('del2').id = 'del3';
};
document.getElementById("deltit").onclick = function deltit() {
document.querySelector('#output2 #alg:last-child').remove();
document.getElementById('add3').id = 'add2';
document.getElementById('del3').id = 'del2';
};
}
// -----------------------------------------
if (document.getElementById("add3")) {
let cont4 = 1;
document.getElementById("add3").onclick = function clone4() {
let container4 = document.getElementById("output3");
let tempLinha4 = document.querySelector('#template2');
let clone4 = document.importNode(tempLinha4.content, true);
const label4 = clone4.querySelector("label");
label4.htmlFor = cont4;
clone4.querySelector("input").className = cont4;
container4.appendChild(clone4);
cont4++;
};
document.getElementById("del3").onclick = function del4() {
document.querySelector('#output3 #linha2:last-child').remove();
};
}
<div class="container">
<button id="addtit">+ TITLE</button>
<button id="deltit">- TITLE</button>
<button id="add2">+ DESCRIPTION</button>
<button id="del2">- DESCRIPTION</button>
<div id="output2"></div>
<div class='botoes'>
<button onclick="result()" id='done'>DONE</button>
</div>
<div class="header"><span class="title">RESULT</span>
</div>
<div class="linha"><textarea id="result"></textarea>
</div>
</div>
<!-- template 2 -->
<template id="template2">
<div class="linha" id="linha2"><div class="coluna1"><label for="0">DESCRIPTION:</label></div><div class="coluna2"><input name="inp2" class="0" type="text"/></div>
</div>
</template>
<!-- template 3 -->
<template id="template3">
<div id="alg">
<div class="linha"><div class="coluna1"><label for="0">TITLE:</label></div><div class="coluna2"><input name="titu" class="0" type="text"/></div>
</div>
<div class="linha" id="linha3"><div class="coluna1"><label for="0">DESCRIPTION:</label></div><div class="coluna2"><input name="inp2" class="0" type="text"/></div>
</div>
<div id="output3"></div>
</div>
</template>
Ok. it's because this part of code in function result:
if (titu[0]) {
for (var k = 0; k < titu.length; k++) {
if (titu[k].value.trim() != '') {
res.value += `<div>
<span>${titu[k].value.trim()}</span>
</div>
<ul>\n`;
for (var j = 0; j < inp2.length; j++) {
if (inp2[j].value.trim() != '') {
res.value += `<li>${inp2[j].value.trim()}</li>\n`;
}
}
}
}
}
your titles have the same names : 'titu' , and your descriptions have same names : 'inp2', and you have two nested loops, for each title, loop on description, and it results as you see.
it's better to change your code and make different names and ids
by the way. if you persist to do not change your code, you should use one loop for both of them, like this code
if (titu[0]) {
for (var k = 0; k < titu.length; k++) {
if (titu[k].value.trim() != '') {
res.value += `<div>
<span>${titu[k].value.trim()}</span>
</div>
<ul>\n`;
if (inp2[k].value.trim() != '') {
res.value += `<li>${inp2[k].value.trim()}</li>\n`;
}
}
}
}
UPDATE
for the case of more description for each title, you have to change the code of onClick methods of Title+ and Description+, the added title and all of its description must have same parent, and after doing that, it's possible to solve the problem like this . (assuming the parent that I already have said has class name 'parent')
function result() {
var parents = document.querySelectorAll(".parent")
parents.forEach(function(parent){
var title = parent.querySelector("titu");
var descriptions = parent.querySelectorAll("inp2");
var res = document.getElementById("result");
if (title.value.trim() != '') {
res.value += `<div>
<span>${title.value.trim()}</span>
</div>
<ul>\n`;
}
descriptions.forEach(function(inp2){
if (inp2.value.trim() != '') {
res.value += `<li>${inp2.value.trim()}</li>\n`;
}
});
});
}
notice that this code could work after modifying Title+ and Description+ events and add same parent with class name parent to title and descriptions inputs

Using jQuery to dynamically create checkboxes based on multiple attributes from div

I would like for jQuery to dyanmically create a list of checkboxes based on the class/data present in divs. Essentially these checkboxes will filter through the products so that clicking a checkbox will show the products containing that tag in their div while avoiding any duplicate checkboxes.
Sample:
<div class="Shoes" data-size="Small" data-color="Black">
<h3>Nike</h3>
</div>
<div class="Belts" data-size="Medium" data-color="Black">
<h3>Belt</h3>
</div>
<div class="Shirt" data-size="Large" data-color="Blue">
<h3>Polo</h3>
</div>
<div class="Socks" data-size="Small" data-color="White">
<h3>Generic Socks</h3>
</div>
Expected output
Class Type
Shoes
Belts
Shirt
Socks
Size
Small
Medium
Large
Color
Black
White
Blue
Each checkbox needs to be able to hide/show the item.
JsFIDDLE
The code I have so far is from searching around/previous answers, however it is only creating 1 checkbox type which is for "class" and not creating multiple ones.
Try
jQuery(function ($) {
function createCheckboxes($els, attr) {
var props = {};
$els.each(function () {
props[$(this).attr(attr)] = true;
});
return $.map(props, function (val, key) {
var $chk = $('<input />', {
type: 'checkbox',
value: key
})
return $('<label />', {
text: key
}).append($chk)
})
}
$('span').append(createCheckboxes($('div'), 'class'))
$('span').append(createCheckboxes($('div'), 'data-size'))
$('span').append(createCheckboxes($('div'), 'data-color'))
});
Demo: Fiddle
Try this http://jsfiddle.net/LzmTA/1/
HTML
<div class="Shoes" data-size="Small" data-color="Black">
<h3>Nike</h3>
</div>
<div class="Belts" data-size="Medium" data-color="Black">
<h3>Belt</h3>
</div>
<div class="Shirt" data-size="Large" data-color="Blue">
<h3>Polo</h3>
</div>
<div class="Socks" data-size="Small" data-color="White">
<h3>Generic Socks</h3>
</div>
Javascript
$(document).ready(function(){
var goods = {};
var divs = $('div');
for(var i = 0; i < divs.length; i++){
var attributes = divs[i].attributes;
var item = {};
for(var j = 0; j < attributes.length; j++){
var attrName = attributes[j].name;
if(!goods[attrName]){
goods[attrName] = {};
}
goods[attrName][attributes[j].value] = 1;
}
}
printAttributes(goods);
console.log(goods);
});
function printAttributes(goods){
for(var group in goods){
var groupTitle = $('<h3>').text(group);
$('span').append(groupTitle);
for(var item in goods[group]){
console.log(item);
var sp = $('<label>').text(item);
var chk = $('<input>').attr('type', 'checkbox').attr('value', item).attr('attr', group);
chk.bind('change', function(){
filterGoods();
});
$('span').append(chk).append(sp);
}
}
}
function filterGoods(){
var separator = '|';
var chks = $('input[type=checkbox]:checked');
var filter = [];
//get filter
for(var i = 0; i < chks.length; i++){
var item = $(chks[i]).attr('attr') + separator + $(chks[i]).val();
filter.push(item);
}
//do filter
var hasAttr = false;
var divs = $('div');
for(var i = 0; i < divs.length; i++){
hasAttr = false;
for(var j = 0; j < filter.length; j++){
var filterParts = filter[j].split(separator);
if($(divs[i]).attr(filterParts[0]) == filterParts[1]){
hasAttr = true;
continue;
}
}
hasAttr ? $(divs[i]).show() : $(divs[i]).hide();
}
console.log(filter);
}

Categories