Get the value of checkboxes in a specific section using javascipt - javascript

I have a page that contain different section these section appear when the user click on li an active class is added to the section and then this section appear
each section contain a box with checkboxes and a link to another page when i click on this link i should store the value of the checkboxes for the section active only to print them later
all the code work fine but my problem is that i only can have the checkbox value for the first section that contain active class by defaul
how can i solve that please?
/*Put active class on li click for section*/
let tabs = document.querySelectorAll(".nav li");
let tabsArray = Array.from(tabs);
let section = document.querySelectorAll(".section");
let sectionArray = Array.from(section);
tabsArray.forEach((ele) => {
ele.addEventListener("click", function (e) {
tabsArray.forEach((ele) => {
ele.classList.remove("active");
});
e.currentTarget.classList.add("active");
sectionArray.forEach((sec) => {
sec.classList.remove("active");
});
document.querySelector('#' + e.currentTarget.dataset.cont).classList.add("active");
});
});
/*put the check box value in localstorage to print them later*/
let printBtn = document.querySelector(".active .btn-print");
let terms = document.querySelectorAll(".active input[type='checkbox']");
let termsValChecked = [];
let termsValUnChecked = [];
printBtn.addEventListener("click", function (e) {
localStorage.removeItem("termschecked");
localStorage.removeItem("termsunchecked");
for (let i = 0; i < terms.length; i++) {
if (terms[i].checked == true) {
termsValChecked.push(terms[i].value);
} else {
termsValUnChecked.push(terms[i].value);
}
}
window.localStorage.setItem("termschecked", JSON.stringify(termsValChecked));
window.localStorage.setItem("termsunchecked", JSON.stringify(termsValUnChecked));
});
.box {
display: flex;
align-items: center;
}
section {
display: none;
}
section.active {
display: block;
}
.nav {
list-style:none;
display: flex;
align-items: center;
}
.nav li {
padding: 20px;
background-color: #ccc;
margin-left: 2px;
cursor: pointer;
}
<ul class="nav">
<li data-cont="r1">1</li>
<li data-cont="r2">2</li>
<li data-cont="r3">3</li>
</ul>
<section class="section section-one active" id="r1">
<h3>Section 1</h3>
<div class="box">
<input type="checkbox" value="test1">
<p>test1</p>
</div>
<div class="box">
<input type="checkbox" value="test2">
<p>test2</p>
</div>
<div class="print">
Print
</div>
</section>
<section class="section section-two" id="r2">
<h3>Section 2</h3>
<div class="box">
<input type="checkbox" value="test3">
<p>test3</p>
</div>
<div class="box">
<input type="checkbox" value="test4">
<p>test4</p>
</div>
<div class="print">
Print
</div>
</section>
<section class="section section-three" id="r3">
<h3>Section 3</h3>
<div class="box">
<input type="checkbox" value="test5">
<p>test5</p>
</div>
<div class="box">
<input type="checkbox" value="test6">
<p>test6</p>
</div>
<div class="print">
Print
</div>
</section>

querySelectorAll returns a static NodeList, i.e. the list will reflect the state at invocation and won't update if the page later changes.
The following line runs when you initialize your page:
let terms = document.querySelectorAll(".active input[type='checkbox']");
And that's why you always capture the first section in local storage.
You need to move this line inside your click handler so that you enumerate the checkboxes inside the .active section at that time.

Remove the Attribute ".change" from your selector on line 23
simply change
let terms = document.querySelectorAll(".active input[type='checkbox']");
to
let terms = document.querySelectorAll("input[type='checkbox']");

Related

Select the first element in javascript

I am beginning with JavaScript and I have a following problem. My webpage by default shows content of all <li> that is Journal Articles, Working papers and Conferences. I would like to have selected the first <li> when I come to the website, that is in this case journal Articles.
Relevant part of the HTML code:
<div class="row" data-aos="fade-up">
<div class="col-lg-12 d-flex justify-content-center">
<ul id="portfolio-flters">
<li data-filter=".filter-journal">Journal articles</li>
<li data-filter=".filter-wp">Working papers</li>
<li data-filter=".filter-conferences">Conferences</li>
</ul>
</div>
</div>
<div class="row portfolio-container" data-aos="fade-up" data-aos-delay="100">
<div class="col-lg-4 col-md-6 portfolio-item filter-journal">
Articles published in journals with IF.
</div>
<div class="col-lg-4 col-md-6 portfolio-item filter-wp">
<li>Misak, V. (2022). Crime and weather: Evidence from the Czech Republic (No. 9/2022). IES Working Paper.</li>
<br>
<li>Garcia-Bernardo, J., Jansky, P., & Misak, V. (2021). Common Agricultural Policy Beneficiaries: Evidence of Inequality from a New Data Set (No. 4/2021). IES Working Paper.</li>
</div>
<div class="col-lg-4 col-md-6 portfolio-item filter-conferences">
<u> 2022: </u>
<li>Young Economists Meeting, Brno, Czech Republic</li>
<li>MAER-Net Colloquium, Kyoto, Japan</li>
</div>
</div>
Javascript function:
window.addEventListener('load', () => {
let portfolioContainer = select('.portfolio-container');
if (portfolioContainer) {
let portfolioIsotope = new Isotope(portfolioContainer, {
itemSelector: '.portfolio-item'
});
let portfolioFilters = select('#portfolio-flters li', true);
on('click', '#portfolio-flters li', function(e) {
e.preventDefault();
portfolioFilters.forEach(function(el) {
el.classList.remove('filter-active');
});
this.classList.add('filter-active');
portfolioIsotope.arrange({
filter: this.getAttribute('data-filter')
});
portfolioIsotope.on('arrangeComplete', function() {
AOS.refresh()
});
}, true);
}
});
How can I modify my JavaScript function to select the first child of by default, please?
Desired output is this:
You code was a bit hard to work with because all the classes are missing and the indents didn't format properly in your question.
I wrote below a full example for how you can achieve the tabs effect your going for below. Hope it helps! I tried to keep it simple and add comments. LMK if you have any questions :)
let currentTab = 0;
let tabItems = document.querySelectorAll('.tabItem');
let tabContents = document.querySelectorAll('.tabContent');
function updateTabs() {
// CYCLE THROUGH EACH tabItems
for (let i = 0; i < tabItems.length; i++) {
tabItems[i].className = i == currentTab ? 'tabItem tabItemSelected' : 'tabItem'; // SET TAB WITH INDEX currentTab TO INCLUDE tabItemSelected CLASS
tabContents[i].style.display = i == currentTab ? 'block' : 'none'; // ONLY SHOW THE CONTENT THAT CORRESPONDS TO INDEX currentTab
}
}
updateTabs(); // LOAD TO STATE 0 --> YOU CAN DO THIS IS document.onload for example
for (let i = 0; i < tabItems.length; i++) {
let curI = i; // CACHE i LOCALLY
tabItems[i].onclick = function() {
currentTab = curI;
updateTabs(); // UPDATE TABS ON CLICK...
}
}
.tabContainer {
width: 100%;
height: 50px;
border-bottom: 1px solid gray;
background-color: #ccc;
}
.tabItem {
display: inline-block;
width: 33.33%;
height: 50px;
line-height: 50px;
text-align: center;
cursor: pointer;
}
.tabItemSelected {
color: blue;
border-bottom: 2px solid blue;
}
<div class="tabContainer">
<div class="tabItem">TAB A</div><div class="tabItem">TAB B</div><div class="tabItem">TAB C</div>
</div>
<br><br>
<div class="tabContent" style="color: red">
Content A
</div>
<div class="tabContent" style="color: blue">
Content B
</div>
<div class="tabContent" style="color: green">
Content C
</div>

Two Column Accordion with Separate Full Width Divs

The intension is to have a two column accordion, without limiting the "expand" field to the left or right column. The catch is that there will be multiple on one page. This is already created, but only button 1 is working. With the way my JS is going, it will get very very repetitive - I am looking for assistance with re-writing the JS to be multiple click friendly. Fiddle: https://codepen.io/ttattini/pen/abLzaaY
EDIT: It would also be perfect if one dropdown would close as the next is opened
HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="row">
<div id="column">
<button id="button">I am Button #1</button>
<button id="button">I am Button #3</button>
</div>
<div id="column">
<button id="button">I am Button #2</button>
<button id="button">I am Button #4</button>
</div>
</div>
<div id="hidden">
<p id="content"> So here I am #1</p>
</div>
<div id="hidden">
<p id="content"> So here I am #2</p>
</div>
<div id="hidden">
<p id="content"> So here I am #3</p>
</div>
<div id="hidden">
<p id="content"> So here I am #4</p>
</div>
CSS
#hidden {
background: #ccc;
margin-top: 2%;
overflow: hidden;
transition: height 200ms;
height: 0; /* <-- set this */
}
#button {
padding: 10px;
margin-top: 5px;
width:50%;
margin-left: 10%;
cursor: pointer;
}
#row {
display: flex;
}
#column {
flex: 50%;
}
JS
$(function() {
var b = $("#button");
var w = $("#hidden");
var l = $("#content");
b.click(function() {
if (w.hasClass('open')) {
w.removeClass('open');
w.height(0);
} else {
w.addClass('open');
w.height(l.outerHeight(true));
}
});
});
The biggest issue is that you're using IDs when you should be using classes. IDs must be unique to each element in a page. When you repeat an ID, JS will only target the first element using that ID. That's why only the first one is working.
The second issue is that, because of the way the script is written, it will only target a single element. What you need to do is get all the elements you want to target by something like their class name and then loop through them, applying the event listener to each one and its appropriate children.
EDIT: Here is an example from some code I wrote for a page with multiple accordions a few weeks ago in vanilla JS
//Below I establish a counting variable and find all the accordions on the page
const acc = document.getElementsByClassName( 'accordion' );
let i;
//Looping through each accordion
for ( i = 1; i <= acc.length; i++ ) {
//Identify target for the event listener. In this case, a heading for each accordion, which I've numbered e.g. "title-1"
const title = 'title-' + i;
const label = document.getElementById( title );
//Identify target content, in this case a list that has a unique ID e.g. "list-1"
const listNum = 'list-' + i;
const list = document.getElementById( listNum );
//Add event listener to heading that toggles the active classes
label.addEventListener( 'click', function() {
label.classList.toggle( 'accordion--active' );
});
}
Of course, there's more than one way to skin a cat, but this is a working example.
I have tracked the clicked event of each button and showed the corresponding hidden content with the use of data- attribute.
I have used vanilla JavaScipt instead of jQuery.
const buttons = document.querySelectorAll('.button');
const hiddens = document.querySelectorAll('.hidden');
buttons.forEach((btn) => {
btn.addEventListener('click', btnClicked)
function btnClicked(e) {
hiddens.forEach((hidden) => {
if(e.target.dataset.btn == hidden.dataset.content) {
hidden.classList.toggle('height')
} else {
hidden.classList.remove('height')
}
})
}
})
.hidden {
background: #ccc;
margin-top: 2%;
padding-left:2%;
overflow: hidden;
transition: height 200ms;
height: 0; /* <-- set this */
}
.hidden.height {
height: 50px;
}
.button {
padding: 10px;
color: white;
background-color: #2da6b5;
border: none;
margin-top: 5px;
width:90%;
margin-left: 5%;
cursor: pointer;
}
.button:hover {
filter: brightness(.9);
}
#row {
display: flex;
}
.column {
flex: 50%;
}
<div id="row">
<div class="column">
<button class="button" data-btn="one">I am Button #1</button>
<button class="button" data-btn="three">I am Button #3</button>
</div>
<div class="column">
<button class="button" data-btn="two">I am Button #2</button>
<button class="button" data-btn="four">I am Button #4</button>
</div>
</div>
<div class="hidden" data-content="one">
<p class="content"> So here I am #1</p>
</div>
<div class="hidden" data-content="two">
<p class="content"> So here I am #2</p>
</div>
<div class="hidden" data-content="three">
<p class="content"> So here I am #3</p>
</div>
<div class="hidden" data-content="four">
<p class="content"> So here I am #4</p>
</div>
Also, please do not use the same ID at multiple elements.

How to correctly target by class name?

I am trying to practice some things on JS, I want to toggle a number of divs on click to change their color but I can't seem to target correctly the first one. It was fine when I did it by tag name but by class it doesnt seem to work. What am I doing wrong? Thanks!
EDIT. This is what my code looks like after your corrections.
<body>
<div class="container">
<div class="one">
</div>
<div class="two">
</div>
<div class="three">
</div>
<div class="four">
</div>
</div>
<script src="script.js"></script>
</body>
let boxOne = document.getElementsByClassName("one")[0]
boxOne.onclick = function() {
alert("Clicked!")
}
I'm going to add that its better to assign an id and use getElementById if the selector is only used by one element.
let boxOne = document.getElementById("one");
let allBoxes = document.getElementsByClassName("square");
boxOne.onclick = function() {
alert("Clicked via ID");
}
const arr = [1, 2, 3];
arr.forEach(i => {
allBoxes[i].onclick = function() {
alert("Clicked via Class");
}
})
.square {
width: 100px;
height: 100px;
background: blue;
margin: 20px;
font-size: 50px;
color: white;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
cursor: pointer;
}
<body>
<div class="container">
<div class="square" id="one">
1
</div>
<div class="square" id="two">
2
</div>
<div class="square" id="three">
3
</div>
<div class="square" id="four">
4
</div>
</div>
</body>
With this line:document.getElementsByClassName(".one")[0]
you are already targeting the div, so change out this:
boxOne[0].onclick =
to this:
boxOne.onclick =
document.
getElementsByClassName returns array of elements with that className (without dot)
querySelector is used for css selectors (eg. ".one", "div.one")
querySelectorAll like 2. but returns array
let boxOne = document.getElementsByClassName("one")[0]
boxOne.onclick = function() {
alert("Clicked!")
}
div {
width: 100px;
height: 100px;
margin: 30px;
background: blue
}
<body>
<div class="container">
<div class="one">
</div>
<div class="two">
</div>
<div class="three">
</div>
<div class="four">
</div>
</div>
<script src="script.js"></script>
</body>

Creating a function for a button that can be used for a certain class using JavaScript

Ultimately, my objective here is to create a JS script where I can use for my quizzes. In a page, I may put more than one item. In this regard, I will have more than one button, one button per item. What you see here is a prototype. (I wanted to see how each element interacts with one another.) In the process of trying to get it work, I looked over what others have done. However, I came to a point where I am stuck and could not see what I am doing wrong. I have a sense of where the problem is but I am not certain of it. Anyway, please see where my mistakes are and let me know.
This is the html part. What I want to happen here is I click the top button and the background of the text, "Hello" at top turns red. I press the middle and the text background turn red, and so on. Currently, I press any button and they all turn red.
<div class="q">
<div class="p">
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>
<div class="q">
<div class="p">
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>
<div class="q">
<div class="p">
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>
The link to the js script is located in the head section.
<script src="../js/s3.js"></script>
Here is the js. I suspect my mistake is in the second half. The reason for this is that I made the var in the second for the same as in the first one and still got the same result. It made me wonder whether if the computer is ignoring this part. I could be wrong. I tried querySelectorAll and the result is the same as well. By the way, when is it advantageous to use querySelectorAll and getElementsByClassName if class is involved?
window.onload = function () {
let c = document.getElementsByClassName('q');
for (var ii = 0; ii < c.length; ii++) {
c[ii].addEventListener('click', function () {
let a = document.getElementsByClassName('p');
for (var i = 0; i < a.length; i++) {
a[i].classList.add('cellRed');
}
});
}
}
Here is the CSS.
.cellGreen {
background-color: green;
color: white;
}
.cellRed {
background-color: red;
color: white;
}
I was told to keep things separate. So I did.
First thing you should do when tackling a problem is to think about what you're trying to accomplish. It really does help to list it in steps.
In every Question div
Get the Button
Get the Paragraph
When the Button is clicked
Turn the Paragraph Red
This allows us to build our code rather simply from step 1 onward. This isn't always perfect, but it is always helpful.
Get All Questions
let questions = document.querySelectorAll(".q");
For Each Question
questions.forEach(question => {
// ... do something
});
Get the Button
questions.forEach(question => {
let btn = question.querySelector("button");
});
Get the Paragraph
questions.forEach(question => {
let btn = question.querySelector("button");
let p = question.querySelector("p");
});
When the Button is Clicked
questions.forEach(question => {
let btn = question.querySelector("button");
let p = question.querySelector("p");
btn.addEventListener("click", function() {
// ... do something
});
});
Turn the Paragraph Red
questions.forEach(question => {
let btn = question.querySelector("button");
let p = question.querySelector("p");
btn.addEventListener("click", function() {
p.classList.add("cellRed");
});
});
Final Example:
window.onload = function() {
let questions = document.querySelectorAll(".q");
questions.forEach(question => {
let btn = question.querySelector("button");
let p = question.querySelector("p");
btn.addEventListener("click", function() {
p.classList.add('cellRed');
});
})
}
.cellGreen {
background-color: green;
color: white;
}
.cellRed {
background-color: red;
color: white;
}
<div class="q">
<div class="p">
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>
<div class="q">
<div class="p">
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>
<div class="q">
<div class="p">
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>
That second loop is unnecessary. Just use let instead of var in first loop.
According to MDN:
let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
Look at example below:
window.onload = function () {
let c = document.getElementsByClassName('q');
for (let i = 0; i < c.length; i++) {
c[i].addEventListener('click', function () {
let a = document.getElementsByClassName('p');
a[i].classList.add('cellRed');
});
}
}
.cellGreen {
background-color: green;
color: white;
}
.cellRed {
background-color: red;
color: white;
}
<div class="q">
<div class="p">
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>
<div class="q">
<div class="p">
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>
<div class="q">
<div class="p" >
<p>Hello</p>
</div>
<button>Show Answer</button>
</div>`
I know you want help with your javascript code, but others have already helped you, so I mostly wanted to show that this can be solved with pure CSS only without javascript. :) I'm basically using radio-buttons and sibling selector.
div.p > p {
margin-bottom: 0;
}
div.p > input {
display: none;
}
div.p > label {
cursor: pointer;
font-size: 90%;
margin-right: 1rem;
}
div.p > input:checked ~ p {
color: white;
background-color: red;
}
div.p > input:checked[value="correct"] ~ p {
background-color: green;
}
<div class="q">
<div class="p">
<input type="radio" name="q1" id="q1_alt1" value="correct" />
<input type="radio" name="q1" id="q1_alt2" />
<p>Hello</p>
<label for="q1_alt1">Answer 1</label>
<label for="q1_alt2">Answer 2</label>
</div>
</div>
<div class="q">
<div class="p">
<input type="radio" name="q2" id="q2_alt1" />
<input type="radio" name="q2" id="q2_alt2" value="correct" />
<p>Hello</p>
<label for="q2_alt1">Answer 1</label>
<label for="q2_alt2">Answer 2</label>
</div>
</div>
<div class="q">
<div class="p">
<input type="radio" name="q3" id="q3_alt1" />
<input type="radio" name="q3" id="q3_alt2" />
<input type="radio" name="q3" id="q3_alt3" value="correct" />
<p>Hello</p>
<label for="q3_alt1">Answer 1</label>
<label for="q3_alt2">Answer 2</label>
<label for="q3_alt3">Answer 3</label>
</div>
</div>

Find number of columns produced by ng-repeat

I have a div on which i used ng-repeat. It displays the list of photos in a grid. This is all very simple stuff and working fine.
I need to add file explorer like navigation on the items inside the grid. The easiest way would be to know the length of a line (number of items in the line) and then do simple math to calculate where to move the selection based on the key pressed.
The problem i'm having is finding out the length of the line. Is it even doable?
Edit :
<div class="images-cq__item" ng-repeat="photo in displayedPhotos">
<div ng-class="{active: photo.selected}" id="{{photo.uuid}}" ng-click="selectionEvent({value: photo.uuid, event: $event, index: $index})">
<div class="images-cq-statut" ng-show="photo.statusCQ != 'none'">
{{photo.statusCQ}}
</div>
<div class="img-cq">
<img ng-src="{{photo.thumbPath100}}" alt="Alternate Text" />
zoom
</div>
<p>
{{photo.title}}
</p>
<ul class="images-cq-tags">
<li id="{{photo.uuid}}.{{tag.value}}" ng-repeat="tag in tags" style="display:none">
<span>{{tag.name}}</span>
</li>
</ul>
</div>
The displayedPhotos is a simple array with photo objects obtained from the server. It contains several links (thumbnails, original), and some other info but i don't think it is relevant in this case.
This is what i got:
JavaScript
var items = $(".images-cq__item");
var previousTop = null;
var itemsPerRow = 0;
for(var i = 0; i < items.length;i++){
var item = items.eq(i);
var offset = item.offset();
var top = offset.top;
if(!previousTop || (top == previousTop)){
previousTop = top;
itemsPerRow++;
} else{
break;
}
}
console.log(itemsPerRow); // 3
HTML
<div class="container">
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
</div>
CSS
.container{
width: 400px;
}
.images-cq__item{
width: 120px;
height: 120px;
background: green;
margin-right: 10px;
margin-bottom: 10px;
display: inline-block;
}
You can use {{$index}} inside ng-repeat
See this Angular.js. How to count ng-repeat iterations which satisfy the custom filter

Categories