Only show div if data attribute has less than a certain number - javascript

Given a number of divs, each with a data attribute of data-episode-count=, each one has a value based on the number of episodes in each container. I'm having trouble hiding a div with class show-more if the number of episodes in the currently selected container (which has the style of display: block) is less than 6. I'm not sure how to check the data-attribute value and if its less than 6, set the div style display to none for the show-more button. There are multiple seasons in the series-seasons-list-wrap class, but I have simplified it for the sake of this question to one.
HTML
<div class="series-seasons-list-wrap">
<div class="js-season-list-item" id="season-1" style="display: block;">
<div class="season-list" data-episode-count="22">
<div class="season-list__item">
<div class="episode-item">
<div class="episode-card js-episode-card">
<div class="episode-card__overlay"><span class="play-circle sm" data-play-path="/play/2603399"><svg class="svg svg-play"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/svg/svg-defs.svg#svg-play">svg-play</use></svg></span></div>
</div>
<div class="episode-details">
<h1 class="heading md hvy">Trick or Treat</h1>
<p></p>
<p class="runtime">22min</p>
</div>
</div>
</div>
<div class="season-list__item">
<div class="episode-item">
<div class="episode-card js-episode-card">
<div class="episode-card__overlay"><span class="play-circle sm" data-play-path="/play/2603400"><svg class="svg svg-play"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/svg/svg-defs.svg#svg-play">svg-play</use></svg></span></div>
</div>
<div class="episode-details">
<h1 class="heading md hvy">The New Man</h1>
<p></p>
<p class="runtime">22min</p>
</div>
</div>
</div>
<div class="season-list__item">
<div class="episode-item">
<div class="episode-card js-episode-card">
<div class="episode-card__overlay"><span class="play-circle sm" data-play-path="/play/2603401"><svg class="svg svg-play"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/svg/svg-defs.svg#svg-play">svg-play</use></svg></span></div>
</div>
<div class="episode-details">
<h1 class="heading md hvy">I'll Give You A Million</h1>
<p></p>
<p class="runtime">22min</p>
</div>
</div>
</div>
<div class="season-list__item">
<div class="episode-item">
<div class="episode-card js-episode-card">
<div class="episode-card__overlay"><span class="play-circle sm" data-play-path="/play/2603402"><svg class="svg svg-play"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/svg/svg-defs.svg#svg-play">svg-play</use></svg></span></div>
</div>
<div class="episode-details">
<h1 class="heading md hvy">Painkiller</h1>
<p></p>
<p class="runtime">22min</p>
</div>
</div>
</div>
<div class="season-list__item">
<div class="episode-item">
<div class="episode-card js-episode-card">
<div class="episode-card__overlay"><span class="play-circle sm" data-play-path="/play/2603403"><svg class="svg svg-play"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/svg/svg-defs.svg#svg-play">svg-play</use></svg></span></div>
</div>
<div class="episode-details">
<h1 class="heading md hvy">The Odds</h1>
<p></p>
<p class="runtime">22min</p>
</div>
</div>
</div>
<div class="season-list__item">
<div class="episode-item">
<div class="episode-card js-episode-card">
<div class="episode-card__overlay"><span class="play-circle sm" data-play-path="/play/2603404"><svg class="svg svg-play"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/svg/svg-defs.svg#svg-play">svg-play</use></svg></span></div>
</div>
<div class="episode-details">
<h1 class="heading md hvy">Mookie and Pookie</h1>
<p></p>
<p class="runtime">22min</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="show-more"><span class="js-show-more-trigger">Show
More</span></div>
The javascript:
const SeriesDetail = {
seasonItems: undefined,
keyContainer: undefined,
keyItems: undefined,
showMoreTrigger: undefined,
activeSeason: undefined,
bindEvents () {
this.keyContainer.addEventListener('click', (e) => {
const target = e.target
const number = (target.dataset && target.dataset.seasonNum) || 1
this.setSeason(number)
this.setHistory(number)
this.toggleShowMore()
})
this.showMoreTrigger.addEventListener('click', (e) => {
this.showMore()
})
},
init () {
this.showMoreTrigger = document.getElementsByClassName('js-show-more-trigger')[0]
this.keyContainer = document.getElementsByClassName('js-season-key-items')[0]
const keyItems = this.keyContainer.querySelectorAll('.js-season-key')
this.keyItems = [...keyItems]
const seasonItems = document.getElementsByClassName('js-season-list-item')
this.seasonItems = [...seasonItems]
const seasonNum = this.getQuery('season') || 1
this.setSeason(seasonNum)
this.bindEvents()
return this
},
setSeason (number) {
this.activeSeason = number
this.seasonItems.map(s => {
s.style.display = s.id === `season-${number}`
? 'block'
: 'none'
})
this.keyItems.map(i => {
if (i.id === `season-key-${number}`) {
i.classList.add('active')
} else {
i.classList.remove('active')
}
})
},
showMore () {
let seasons = document.getElementsByClassName('js-season-list-more')
seasons = [...seasons]
let season = seasons.find(s => {
const number = s.dataset.seasonNumber
return this.activeSeason === number
})
if (season) {
season.style.display = season.style.display === 'flex' ? 'none' : 'flex'
}
},
toggleShowMore() {
let showMore = document.getElementsByClassName("show-more")[0];
let seasonList = document.getElementsByClassName("season-list")
let episodeCount = [].slice.call( seasonList ).reduce((acc,val) => {
return acc + Number(val.getAttribute("data-episode-count")) }, 0)
for(let i = 0; i < document.getElementsByClassName("series-seasons-list-wrap").length; i++){
if(seasonList.getAttribute("data-episode-count") < 6){
showMore.style.display = 'none'
}
}
}
}
document.addEventListener('DOMContentLoaded', () => {
SeriesDetail.init()
})
Codepen: https://codepen.io/testermytesty/pen/wmdGZE?editors=1010

The issue you have is in your Javascript. In the line:
let episodeCount = seasonList.getAttribute("data-episode-count");
seasonList is an HTMLCollection. If you have only one and you want to get the desired attribute, you should do:
let episodeCount = seasonList[0].getAttribute("data-episode-count");
If it is more than one element in the seasonList Collection, then you can use:
let episodeCount = [].slice.call(seasonList).reduce((acc,val) => {
return acc + Number(val.getAttribute("data-episode-count"))
}, 0)

Related

How to achieve level 3 div with javascript and apply styling

Hello I would like to reach a level 3 div and change the style of this div
in my example I would therefore like to be able to apply disply:none on style color red
to make the word Warning invisible
<div id="Zone">
<div class="MR-Widget ">
<div class="Title"> </div>
<div class="Errors" style="display: none"></div>
<div class="Content">
<div class="search"> </div>
<div class="resultat" style="width: 120px;"></div>
<div class="MR" id="Lock" style="display: none;"> </div>
<div style="color: red"> Warning </div>
</div>
</div>
</div>
To select 3rd level div:
document.querySelector('#Zone > div > div > div')
Now the problem is you have 4 div at 3rd level. So needed to select all and check style color. That gives:
const warningNone = () => {
Array.from(document.querySelectorAll('#Zone > div > div > div')).forEach(el => {
if (el) {
if (el.style.color === 'red') {
el.style.display = 'none';
}
}
})
}
window.addEventListener('load', warningNone);
<div id="Zone">
<div class="MR-Widget ">
<div class="Title"> </div>
<div class="Errors" style="display: none"></div>
<div class="Content">
<div class="search"> </div>
<div class="resultat" style="width: 120px;"></div>
<div class="MR" id="Lock" style="display: none;"> </div>
<div style="color: red"> Warning </div>
</div>
</div>
</div>
I modified the snippet to check the >div>div>div existence
By the way, I put the function to be fired when document loaded, otherwise your red will not apply
3...
try to split the query line in 2:
const warningNone = () => {
const els = document.querySelectorAll('#Zone > div > div > div');
els.forEach(el => {
if (el.style.color === 'red') {
el.style.display = 'none';
}
})
}
window.addEventListener('load', warningNone);
now in dev tools check which line fire the error

Change the Page layout when a button is clicked

I want to change the layout of a page that has 3 columns:
<div>
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
... to 4 columns when a button is clicked:
<div>
<div class="container">
<div class="row" >
<div class="col-md-3"></div>
<div class="col-md-3"></div>
<div class="col-md-3"></div>
<div class="col-md-3"></div>
</div>
</div>
</div>
I have no clue on how to do this.
There are many ways you can add another div. Here is my approach :
function appendDiv(){
let row = document.getElementsByClassName('row');
// change className for all the col-md-4 div
document.querySelectorAll('.col-md-4').forEach(function(item) {
item.className = 'col-md-3';
})
//create new div;
let col = document.createElement('div');
// add classname to div
col.className = "col-md-3"
row[0].appendChild(col)
}
.col-md-4{
border : 1px solid blue;
height : 20px;
}
.col-md-3{
border : 1px solid green;
height : 20px;
}
<div>
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
<button onClick='appendDiv()'>click</button>
</div>
</div>
There's a few ways this could be done depending on your data, however, here's one angle.
If you have both your 4 column & 3 column versions of the data loaded on the page (but one hidden with css). You could run something like this.
HTML
<div id="colsThree" class="displayArea show">
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
<div id="colsFour" class="displayArea">
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
<button id="changeColumns">Click Me To Change Columns</button>
Javascript
const buttonEl = document.querySelector("#changeColumns");
buttonEl.addEventListener('click', () => {
const outputEls = document.querySelectorAll('.displayArea')
outputEls.forEach((outputEl) => {
outputEl.toggle("show")
})
});
CSS
.displayArea {
display: none;
}
.displayArea.show {
display: block;
}
Use forEach and appendChild method.
const btn = document.querySelector('#btn')
btn.onclick = function() {
const targetClasses = document.querySelectorAll('.col-md-4')
targetClasses.forEach((tag, idx) => {
tag.className = 'col-md-3'
const lastIdx = targetClasses.length - 1
if (idx === lastIdx) {
const tag = document.createElement('div')
, row = document.querySelector('.row')
tag.className = 'col-md-3'
tag.innerText = '4'
row.appendChild(tag)
}
})
console.log(targetClasses)
return
}
<div>
<button id="btn">Click me</button>
<div class="container">
<div class="row" >
<div class="col-md-4">1</div>
<div class="col-md-4">2</div>
<div class="col-md-4">3</div>
</div>
</div>
</div>
If you're only using vanilla HTML, CSS, and JavaScript, then one of the ways to achieve this is by adding a click listener to the button beforehand. FYI: for brevity's sake, I'll call the div element with row class as parent. When user clicks the button, then it should
remove col-md-4 class and add col-md-3 class to all the children elements of parent.
add a new div element with col-md-3 class into parent.
Here's a link to the codepen for your reference.
const button = document.querySelector('button');
const rowDiv = document.querySelector('.row');
button.addEventListener('click', (e) => {
Array.from(rowDiv.children).forEach(childDiv => {
childDiv.classList.remove('col-md-4');
childDiv.classList.add('col-md-3');
});
const newDiv = document.createElement('div');
newDiv.classList.add('col-md-3');
rowDiv.appendChild(newDiv);
// I disabled the button to prevent the user
// from clicking it the second time.
e.target.disabled = true;
});
.button-parent {
margin: 15px 0;
}
.row {
height: 100vh;
}
.row > div:nth-child(1) {
background: red;
}
.row > div:nth-child(2) {
background: blue;
}
.row > div:nth-child(3) {
background: yellow;
}
.row > div:nth-child(4) {
background: green;
}
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
<div>
<div class="container">
<div class="button-parent">
<button class="btn btn-primary">Add div</button>
</div>
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
</body>

Puppeteer- Need help to extract the text from h2 and span

Absolute beginner here with JS. I need help to extract the text from DOM which looks like this.
Extracting can be done by querySelectorAll() or getElementsByTagName(). But what I'm looking for is to create an object with each h2 element as the key and the span as it's value. I don't have an idea of how this can be achieved. Any suggestions would be very helpful.
<div class ="product-list">
<div class="row column">
<div class="column medium-9 large-10">
<h2 class="product-name">Products List 1</h2>
</div>
</div>
<div class="row">
<span>First Product</span>
</div>
<div class="row">
<span> Second Product</span>
</div>
.
.
.
<div class="row">
<span>
Nth Product
</span>
</div>
<div class="row column">
<div class="column medium-9 large-10">
<h2 class="product-name">Products List 2</h2>
</div>
</div>
<div class="row">
<span>Thrid Product</span>
</div>
<div class="row">
<span> Fourth Product</span>
</div>
.
.
.
<div class="row">
<span>
Nth Product
</span>
</div>
</div>
From this DOM I need to store the data as
[
Products List 1 :[First Product,Second Product...Nth Product],
Products List 2 :[Third Product,Fourth Product...Nth Product]
]
JS:
const products=await page.evaluate(()=>{
const productsArray=[];
var pdName1=document.querySelectorAll('div.column > h2.product-name');
var pdName2=document.querySelectorAll("div.row > span")
pdName2.forEach(query=>{
productArray.push(query.innerText)
})
return productArray
})
You can try something like this:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const html = `
<!doctype html>
<html>
<head><meta charset='UTF-8'><title>Test</title></head>
<body>
<div class ="product-list">
<div class="row column">
<div class="column medium-9 large-10">
<h2 class="product-name">Products List 1</h2>
</div>
</div>
<div class="row"><span>First Product</span></div>
<div class="row"><span> Second Product</span></div>
<div class="row"><span>Nth Product</span></div>
<div class="row column">
<div class="column medium-9 large-10">
<h2 class="product-name">Products List 2</h2>
</div>
</div>
<div class="row"><span>Thrid Product</span></div>
<div class="row"><span> Fourth Product</span></div>
<div class="row"><span>Nth Product</span></div>
</div>
</body>
</html>`;
try {
const [page] = await browser.pages();
await page.goto(`data:text/html,${html}`);
const data = await page.evaluate(() => {
const elements = document.querySelectorAll('h2, div.row span');
const list = {};
let currentKey = null;
for (const element of elements) {
if (element.tagName === 'H2') {
currentKey = element.innerText;
list[currentKey] = [];
} else {
list[currentKey].push(element.innerText);
}
}
return list;
});
console.log(data);
} catch (err) { console.error(err); } finally { await browser.close(); }

pure JS handling if content text length < 0 then hide

I tried to handling,
if content text length < 0
then set class match-name & divider style to display none.
it seem like not working with below code.
var newsMatchName = document.querySelectorAll(".match-name");
var newsMatchNameDivider = document.querySelectorAll(".divider");
for (var i = 0; i < newsMatchName.length; i++) {
if (newsMatchName[i].length < 0) {
console.log(newsMatchName[i].length)
for (var i = 0; i < elem.length; i++) {
newsMatchName[i].style.display = "none";
newsMatchNameDivider[i].style.display = "none"
}
}
}
<div class="news-content">
<div class="match-name"></div>
<div class="divider"></div>
<div class="title">Title Text</div>
<div class="footer">
<span class="author">Author</span>
<span class="spacer"></span>
<div class="timeline">
<span class="date">2021-04-01</span>
<span class="time">12:15</span>
</div>
</div>
</div>
I have corrected the code for you:
var newsMatchName = document.querySelectorAll(".match-name");
var newsMatchNameDivider = document.querySelectorAll(".divider");
for (var i = 0; i < newsMatchName.length; i++) {
if (newsMatchName[i].innerText.length <= 0) {
newsMatchName[i].style.display = "none";
newsMatchNameDivider[i].style.display = "none"
}
}
<div class="news-content">
<div class="match-name"></div>
<div class="divider"></div>
<div class="title">Title Text</div>
<div class="footer">
<span class="author">Author</span>
<span class="spacer"></span>
<div class="timeline">
<span class="date">2021-04-01</span>
<span class="time">12:15</span>
</div>
</div>
</div>
I test the length of the text in match-name
Length can be 0 or more, not <0, so use
newsMatchName[i].textContent.length === 0
but I suggest this:
var newsMatchName = document.querySelectorAll(".match-name");
var newsMatchNameDivider = document.querySelectorAll(".divider");
newsMatchName.forEach((name, i) => {
const textLen = name.textContent.trim().length;
name.classList.toggle("hide", textLen === 0);
newsMatchNameDivider[i].classList.toggle("hide", textLen === 0);
})
.hide {
display: none;
}
<div class="news-content">
<div class="match-name"></div>
<div class="divider">This will be hidden</div>
<div class="title">Title Text</div>
<div class="footer">
<span class="author">Author</span>
<span class="spacer"></span>
<div class="timeline">
<span class="date">2021-04-01</span>
<span class="time">12:15</span>
</div>
</div>
</div>

Have an element react to another element change of state

I have two divs, wrapping 8 divs each:
<div class="binaries">
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
</div>
<div class="numbers">
<div class="each-number" data-value="128"> 128 </div>
<div class="each-number" data-value="64"> 64 </div>
<div class="each-number" data-value="32"> 32 </div>
<div class="each-number" data-value="16"> 16 </div>
<div class="each-number" data-value="8"> 8 </div>
<div class="each-number" data-value="4"> 4 </div>
<div class="each-number" data-value="2"> 2</div>
<div class="each-number" data-value="1"> 1 </div>
</div>
What I want to achieve, is that clicking, for example, the first div in the numbers section, changes the first div in the binaries section, but I'm not sure how to link them without adding a click handler to every single div in the numbers section.
Codepen:
http://codepen.io/Hyde87/full/zNGXXw/
JS:
"use strict";
let count = 0;
const output = document.getElementById("output");
const gameResult = document.getElementById("gameResult");
const numbers = document.querySelector(".numbers");
const binaries = document.querySelectorAll(".binary-number");
const randomizer = document.querySelector(".randomizer");
/* Get the number value of every number on click using event delegation, then call the testValue function */
numbers.addEventListener("click", getValue);
function getValue(e){
if (e.target.className == "each-number") {
e.target.classList.add("light");
let thisValue = e.target.getAttribute('data-value');
count += parseInt(thisValue);
console.log(count);
testValue()
}}
/* The values are added to the count variable, which is tested against the random number */
function testValue(){
if (count > parseInt(output.textContent)) {
gameResult.textContent = "Wrong value, you went over it."
count = 0;
output.textContent = "";
} else if (count === parseInt(output.textContent)) {
gameResult.textContent = "You got it right!";
output.textContent = "";
}
}
/* Gets a random number between 1 and 128 */
function getRandom() {
return Math.floor(Math.random() * (128 - 1 + 1)) + 1;
}
/* Displays the random number and resets other values so we always start from scratch when we get a new random number */
randomizer.addEventListener("click", function() {
gameResult.textContent = "";
count = 0;
output.textContent = getRandom();
for (let i = 0; i < binaries.length; i++) {
binaries[i].textContent = "0";
}
})
Add classes to binaries as
<div class="binaries">
<div class="binary-number num-128"> 0 </div>
<div class="binary-number num-64"> 0 </div>
<div class="binary-number num-32"> 0 </div>
<div class="binary-number num-16"> 0 </div>
<div class="binary-number num-8"> 0 </div>
<div class="binary-number num-4"> 0 </div>
<div class="binary-number num-2"> 0 </div>
<div class="binary-number num-1"> 0 </div>
</div>
And add these two line in getValue function
let binaryElem = document.querySelector(".binary-number.num-"+thisValue);
binaryElem.textContent = "1";
You don't have to add a click handler to every element inside numbers. Just add it to the parent element and get the index of the clicked event target.
Here is an example.
var bin = document.getElementsByClassName('binaries')[0];
var num = document.getElementsByClassName('numbers')[0];
function numClick(evt) {
var index = 0;
for(var i = num.children.length - 1; i >= 0; i--) {
if (evt.target == num.children[i]) {
index = i;
}
}
bin.children[index].classList.add('mark');
}
num.addEventListener('click', numClick)
.mark {
color: white;
background: red;
}
.binaries , .numbers {
display: flex;
justify-content: space-between;
}
<div class="binaries">
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
<div class="binary-number"> 0 </div>
</div>
<div class="numbers">
<div class="each-number" data-value="128"> 128 </div>
<div class="each-number" data-value="64"> 64 </div>
<div class="each-number" data-value="32"> 32 </div>
<div class="each-number" data-value="16"> 16 </div>
<div class="each-number" data-value="8"> 8 </div>
<div class="each-number" data-value="4"> 4 </div>
<div class="each-number" data-value="2"> 2</div>
<div class="each-number" data-value="1"> 1 </div>
</div>

Categories

Resources