Vanilla JS Find Element with Missing Child Element - javascript

I am trying to get a boolean that will let me know if a parent element has a child element. Some of the constraints are:
There can be x number of these "boxes"
Check and see for any of the boxes have a footer element
If the box does not have a footer element, add a class
<div class="data-trend">
<header></header>
<div class="main"></div>
<footer></footer>
</div>
<div class="data-trend">
<header></header>
<div class="main"></div>
<footer></footer>
</div>
<div class="data-trend">
<header></header>
<div class="main">
<span class="glyphicons"><span>
<!-- On the glyphicons I would add class if there is no footer -->
</div>
<!-- notice no footer -->
</div>

Moved the above comment into an actual answer:
var elems = document.querySelectorAll('.data-trend'),
len = elems.length,
i = -1
while(++i < len) {
if(!elems[i].querySelector('footer')) elems[i].classList.add('no-footer')
}
.no-footer { border: 3px solid blue; }
<div class="data-trend">
<header>Testing</header>
<div class="main"></div>
<footer>Footer</footer>
</div>
<div class="data-trend">
<header>Testing</header>
<div class="main"></div>
<footer>Footer</footer>
</div>
<div class="data-trend">
<header>Testing</header>
<div class="main">
<span class="glyphicons"><span>
</div>
</div>
If you're doing a lot of this in vanilla, it makes sense to define a forEach helper function for yourself
function forEach(elems, fn){
var len = elems.length,
i = -1
while(++i < len) {
fn(elems[i])
}
}

Related

How to make this multi-slide panel loop indefinitely

I have three panels that have just text or text and images that I want to loop indefinitely, and be scalable from 1-1000 slides.
I have the following markup:
<div class="mb-panel-container cf">
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">
<h1>1.1</h1>
<p>slide 1.1</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>1.2</h1>
<p>slide 1.2</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>1.3</h1>
<p>slide 1.3</p>
<p class="datetime"></p>
</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">
<h1>2.1</h1>
<p>slide 2.1</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>2.2</h1>
<p>slide 2.2</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>2.3</h1>
<p>slide 2.3</p>
<p class="datetime"></p>
</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">
<h1>3.1</h1>
<p>slide 3.1</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>3.2</h1>
<p>slide 3.2</p>
<p class="datetime"></p>
</div>
<div class="mb-panel">
<h1>3.3</h1>
<p>slide 3.3</p>
<p class="datetime"></p>
</div>
</div>
</div>
And the following script:
<script>
$(document).ready(function() {
var items = $(".mb-panel"),
currentItem = items.filter(".active");
window.setInterval( function() {
var nextItem = currentItem.next();
currentItem.removeClass("active");
if( nextItem.length ) {
currentItem = nextItem.addClass("active");
} else {
currentItem = items.first().addClass("active");
}
}, 5000);
});
</script>
Unfortunately I am ending up with something like this:
Essentially, the first run of the panels work, but when it gets to the loops it stops for the other panels apart from column 1. I will be opening this up to allow the users to add as many notices per panel as they need, but require it to loop back to the beginning for each column once it reaches the last slide.
You have to pick the exact element with .eq(index) and change the index, depending - if it reached the max allowed length.
$('.mb-slider').each(function(){ // looping for each slider block
let panels = $(this).find('.mb-panel'); // collecting current slides
let len = panels.length;
let index = 0;
setTimeout(function loop(){
panels.eq(index).removeClass('active');
index = (index == len - 1) ? 0 : index + 1; // Google → Ternary operator
panels.eq(index).addClass('active');
setTimeout(loop, 1000);
}, 1000);
});
.mb-panel {
display: none;
border: 2px solid orange;
margin: 5px;
padding: 5px;
}
.mb-panel.active { display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="mb-panel-container cf">
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">1-1</div>
<div class="mb-panel">1-2</div>
<div class="mb-panel">1-3</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">2-1</div>
<div class="mb-panel">2-2</div>
<div class="mb-panel">2-3</div>
<div class="mb-panel">2-4</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">3-1</div>
<div class="mb-panel">3-2</div>
<div class="mb-panel">3-3</div>
<div class="mb-panel">3-4</div>
<div class="mb-panel">3-5</div>
</div>
</div>
I've used self-calling setTimeout chain just because like that trick. Here you can use setInterval as well. But in some cases, it make sense - not to call function repetitively, while the previous step haven't completed yet.
Translated into native JS ( find 10 differences :D ):
let slider = document.querySelectorAll('.mb-slider');
for( let i = 0; i < slider.length; i++ ){
let panels = slider[i].querySelectorAll('.mb-panel');
let len = panels.length;
let index = 0;
setTimeout(function loop(){
panels[index].classList.remove('active');
index = (index == len - 1) ? 0 : index + 1;
panels[index].classList.add('active');
setTimeout(loop, 1000);
}, 1000);
}
.mb-panel {
display: none;
border: 2px solid orange;
margin: 5px;
padding: 5px;
}
.mb-panel.active { display: block; }
<div class="mb-panel-container cf">
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">1-1</div>
<div class="mb-panel">1-2</div>
<div class="mb-panel">1-3</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">2-1</div>
<div class="mb-panel">2-2</div>
<div class="mb-panel">2-3</div>
<div class="mb-panel">2-4</div>
</div>
<div class="mb-panel-section mb-slider">
<div class="mb-panel active">3-1</div>
<div class="mb-panel">3-2</div>
<div class="mb-panel">3-3</div>
<div class="mb-panel">3-4</div>
<div class="mb-panel">3-5</div>
</div>
</div>

How can I toggle a class to various items?

I just want to toggle a class from various items. I mean, I have 5 divs with .item class inside a div with .container class. Can I toggle a new class to those 5 .items? Maybe with a loop?
I was trying to do it but it seems that the loop goes infinite.
This is what I tried:
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<button onclick="myFunction()"></button>
</div>
<script>
function myFunction () {
var x = document.getElementsByClassName("item");
for (i=0; i<x.length; i+1) {
x[i].classList.toggle('new-class');
}
}
</script>
I expect something like this at the end (maybe with some loop in JS):
<div class="container">
<div class="item new-class"></div>
<div class="item new-class"></div>
<div class="item new-class"></div>
<div class="item new-class"></div>
<div class="item new-class"></div>
</div>
You can do it by using the for loop and the classList.add() method.
function myFunction() {
var items = document.querySelectorAll('.item');
for(var i = 0; i < items.length; i++) {
items[i].classList.add('new-class');
}
}

Javascript function to rotate some div elements on page

I have some javascript function - shows me a popup with some texts. I try to rotate two "section" elements, but if I add to HTML one more section with class custom, the page shows only first element. Please, help me to add 1-2 more elements and to rotate it. The idea is to have 2 or more elements with class custom and to show it in random order, after last to stop. Thanks.
setInterval(function () {
$(".custom").stop().slideToggle('slow');
}, 2000);
$(".custom-close").click(function () {
$(".custom-social-proof").stop().slideToggle('slow');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="custom">
<div class="custom-notification">
<div class="custom-notification-container">
<div class="custom-notification-image-wrapper">
<img src="checkbox.png">
</div>
<div class="custom-notification-content-wrapper">
<p class="custom-notification-content">
Some Text
</p>
</div>
</div>
<div class="custom-close"></div>
</div>
</section>
Set section display none of page load instead of first section. Check below code of second section:
<section class="custom" style=" display:none">
<div class="custom-notification">
<div class="custom-notification-container">
<div class="custom-notification-image-wrapper">
<img src="checkbox.png">
</div>
<div class="custom-notification-content-wrapper">
<p class="custom-notification-content">
Mario<br>si kupi <b>2</b> matraka
<small>predi 1 chas</small>
</p>
</div>
</div>
<div class="custom-close"></div>
</div>
</section>
And you need to make modification in your jQuery code as below:
setInterval(function () {
var sectionShown = 0;
var sectionNotShown = 0;
$(".custom").each(function(i){
if ($(this).css("display") == "block") {
sectionShown = 1;
$(this).slideToggle('slow');
} else {
if (sectionShown == 1) {
$(this).slideToggle('slow');
sectionShown = 0;
sectionNotShown = 1;
}
}
});
if (sectionNotShown == 0) {
$(".custom:first").slideToggle('slow');
}
}, 2000);
Hope it helps you.

Close a bootstrap row and start new row after every nth containing div

I have a bootstrap row which will be populated by, let's say, blog post thumbnails.
<section class="container">
<div class="row thumbs">
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
</div>
<hr class="divider" />
<div class="navigation">navigation</div>
</section
I want to close a row, insert hr tag and open a new bootstrap row after every 4th post thumbnail.
<section class="container">
<div class="row thumbs">
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
</div>
<hr class="divider" />
<div class="row">
<div class="col-sm-3">content</div>
</div>
<hr class="divider" />
<div class="navigation">navigation</div>
</section>
Is there a way to do this with jquery?
Can do something like this:
var $mainElem = $('.row'),/* adjust selector to suit page*/
$parent = $mainElem.parent(),
/* remove children after 4th from existing row */
$items = $mainElem.children(':gt(3)').detach();
if ($items.length) {
/* create new row for every 4 items removed above */
for (var i = 0; i < $items.length; i = i + 4) {
var $row = $('<div class="row">').append($items.slice(i, i + 4));
$parent.append('<hr class="divider">').append($row);
}
}
DEMO
This worked best for me:
var $d = $('.thumbs');
var $p = $('.col-sm-3:gt(3)', $d);
if ($p.length) {
$('<div class="row thumbs">').insertAfter($d).append($p);
$('<hr class="divider">').insertAfter($d);
}
Depending on your motivation to wrap each set of columns in a new row, you can style every nth row with straight CSS.
In bootstrap, if you have extra columns that spillover past 12, they just wrap into a new line anyway, so having the new row is usually redundant, although you might have some external reason to keep it in your case.
Either way, here's a CSS solution that adds a page wide horizontal divider every 4 divs:
Demo in jsFiddle & Stack Snippets
.container .row.thumbs div:nth-child(4n) {
position: static;
}
.container .row.thumbs div:nth-child(4n):after {
content: ' ';
border-top: 1px solid black;
display: block;
position: absolute;
width: 95%;
margin-left: 2.5%;
left: 0;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"/>
<section class="container">
<div class="row thumbs">
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
<div class="col-sm-3">content</div>
</div>
<div class="navigation">navigation</div>
</section>
Also, bear in mind that this won't be natively supported in < IE8

Conditional innerhtml change

Let's say I have an HTML structure like this:
<li id="jkl">
<div class="aa">
<div class="bb">
<div class="cc">
<div class="dd">
<a ...><strong>
<!-- google_ad_section_start(weight=ignore) -->Test
<!-- google_ad_section_end --></strong></a>
</div>
</div>
</div>
<div class="ee">
<div class="ff">
<div class="gg">
<div class="excludethis">
<a...>Peter</a>
</div>
</div>
</div>
</div>
</div>
</li>
My goal is to set the content(innerhtml) of <li id="jkl"> to '' if inside of <li id="jkl"> there is any word of a list of words(In the example below, Wortliste) except when they are in <div class="excludethis">.
In other words, ignore <div class="excludethis"> in the checking process and show the html even if within <div class="excludethis"> there are one or more words of the word list.
What to change?
My current approach(that does not check for <div class="excludethis">)
Wortliste=['Test','Whatever'];
TagListe=document.selectNodes("//li[starts-with(#id,'jk')]");
for (var Durchgehen=TagListe.length-1; Durchgehen>=0; Durchgehen--)
{
if (IstVorhanden(TagListe[Durchgehen].innerHTML, Wortliste))
{
TagListe[Durchgehen].innerHTML = '';
}
}
with
function IstVorhanden(TagListeElement, Wortliste)
{
for(var Durchgehen = Wortliste.length - 1; Durchgehen>=0; Durchgehen--)
{
if(TagListeElement.indexOf(Wortliste[Durchgehen]) != -1)
return true;
}
return false;
}
Only has to work in opera as it's an userscript.

Categories