I have this code example:
<section class="timeline">
<article class="post ">
<article class="post ">
<article class="post ">
<article class="post ">
</section>
Now i want to know how to add a class via javascript to article element.
For example:
1st article add class "left"
2nd article add class "right"
3rd article add class "left"
4th article add class "right"
I am not sure really what you want to do.I hope This will help you.
let article = document.querySelectorAll('.post');
article.forEach((item, index) => {
(index % 2 == 0) ?item.classList.add('odd'):item.classList.add('even')
});
I'm not sure what you really want to do but very probably you don't need have any javascript you can write styles for odd and even childrens.
.post:nth-child(odd) {
color: green;
}
.post:nth-child(even) {
color: red;
}
<section class="timeline">
<article class="post ">Article</article>
<article class="post ">Article</article>
<article class="post ">Article</article>
<article class="post ">Article</article>
</section>
The answer of Czlowiek is in my opinion the best answer, because it does not require Javascript to be enabled.
Next is that you should use ids. It is certainly a logical attribute for sections, but it is also very logical for articles.
But if you would like to do this with Javascript, then should you first get a handle on the section tag, with for instance:
var sec = document.getElementById('timeline');
Next can you loop through the childNodes of the section like:
var cntArticle = 0;
for(var i = 0; i < sec.childNodes.length; i++) {
if(sec.childNodes[i].tagName === 'ARTICLE') {
if(cntArticle%2 === 0){
sec.childNodes[i].className += ' left';
} else {
sec.childNodes[i].className += ' right';
}
cntArticle++;
}
}
Related
I am trying to iterate through the parent class "cardTags" to change the background color of each child div "tag" depending on what the value is (using .textContent)
For HTML I have:
<div class = 'card'>
<div class='cardTags'>
<div class='tag' id='tag1'>
<header> Auction </header>
</div>
<div class='tag' id='tag2'>
<header> 3d 40m 23s left </header>
</div>
<div class='tag' id='tag3'>
<header> $39 </header>
</div>
</div>
</div>
<div class = 'card'>
<div class='cardTags'>
<div class='tag' id='tag1'>
<header> Sell </header>
</div>
<div class='tag' id='tag2'>
<header> Used </header>
</div>
<div class='tag' id='tag3'>
<header> $59 </header>
</div>
</div>
</div>
For Javascript
function checkTags() {
var category = document.getElementById('tag1');
var condition = document.getElementById('tag2');
var specialty = document.getElementById('tag3');
var textCategory = category.textContent;
var textCondition = condition.textContent;
var textSpecialty = specialty.textContent;
if (textCategory = "Auction") {
category.style.backgroundColor = "#00FF00";
} else if (textCategory = "Trade" {
category.style.backgroundColor = "#00FF00";
} else {
category.style.backgroundColor = "#00FF00";
}
if (textCondition.length = 'Used') {
condition.style.backgroundColor = '#f75555';
} else if (textCondition = 'New') {
condition.style.backgroundColor = '#2fb62f';
} else {
condition.style.backgroundColor = '#f9f906';
}
}
I know the javascript above will only look at 1 div "cardTags" not all the other ones, which is why I am trying to know how can I iterate through each "cardTags" div and see the child divs inside and change the background colors of those divs depending on the values within them. Currently javascript only recognizes one set.
1. id attributes should be unique, currently you have the same id on more than one child element of the parent divs. So you should use the shared className for the children.
2. I modified the classNames of each child tag to be tag1, tag2, tag3, respectively on each set of children.
3. You had some typo's or badly formatted code as well, where you were missing a parenthesis on one of your else-if statements.
4. You were assigning values instead of doing equality comparison in your if and else-if, so I fixed that as well.
5. Also, you were attempting to do comparison of string values and the text content of the headers had leading and trailing space, so I added the trim() function on each call to textContent to remove the extra whitespace for equality comparison.
6. You also had one string comparison where you had appended .length at the end of the string variable, which was causing issues as well.
7. Please see the following for a working example:
window.addEventListener("DOMContentLoaded", () => {
checkTags();
});
function checkTags() {
//get the parents
const parents = document.querySelectorAll('div.cardTags');
parents.forEach((el) => {
const category = el.querySelector('div.tag1');
const condition = el.querySelector('div.tag2');
const specialty = el.querySelector('div.tag3');
const textCategory = category.querySelector('header').textContent.trim();
const textCondition = condition.querySelector('header').textContent.trim();
const textSpecialty = specialty.querySelector('header').textContent.trim();
if (textCategory === "Auction") {
category.style.backgroundColor = "#00FF00";
} else if (textCategory === "Trade") {
category.style.backgroundColor = "#00FF00";
} else {
category.style.backgroundColor = "#00FF00";
}
if (textCondition === 'Used') {
condition.style.backgroundColor = '#f75555';
} else if (textCondition === 'New') {
condition.style.backgroundColor = '#2fb62f';
} else {
condition.style.backgroundColor = '#f9f906';
}
});
}
<div class = 'card'>
<div class='cardTags'>
<div class='tag1'>
<header> Auction </header>
</div>
<div class='tag2'>
<header> 3d 40m 23s left </header>
</div>
<div class='tag3'>
<header> $39 </header>
</div>
</div>
</div>
<div class = 'card'>
<div class='cardTags'>
<div class='tag1'>
<header> Sell </header>
</div>
<div class='tag2'>
<header> Used </header>
</div>
<div class='tag3'>
<header> $59 </header>
</div>
</div>
</div>
I want to apply function to multiple div pairs in multiple wrappers. Divs should be selected in parallel by order from 2 different classes.
The best what I can think of is to make an array with n pairs of divs from n number of modules, but I don't know if the concept itself and syntax is right.
Now, I want to apply function to first/second/third/... object-1 and descript-1 divs inside only one module at the same time. And the same goes for next module, instead function should be applied to object-2 - descript-2 pair.
Updated code:
Now I have three different functions, one for next-prev buttons, one for thumbnail control and last one for showing object/description class divs and highlighting thumbs.
I've made nested functions attempt but it doesn't work. Should I declare vars, and get content before making 3 separate modules.forEach functions?
<script>
// nodes Array
let modules = Array.prototype.slice.call(document.querySelectorAll(".module"));
// Loop over the modules without index.
modules.forEach(function(module){
var divIndex = 1;
showDivs(divIndex);
// Objects, descr, thumbs
let objects = module.querySelectorAll(".object");
let descripts = module.querySelectorAll(".descript");
let thumbs = module.querySelectorAll(".thumb");
// next-prev buttons
function plusDivs(n) {
showDivs(divIndex += n);
}
// thumb control
function currentDiv(n) {
showDivs(divIndex = n);
}
// div display
function showDivs(n) {
if (n > objects.length) {divIndex = 1}
if (n < 1) {divIndex = objects.length}
// hide content, shade thumb
objects.style.display = "none";
descripts.style.display = "none";
thumbs.className = thumbs.className.replace(" active", "");
// show only selected object-descr pair and highlight thumb
for(var i = 0; i < objects.length; i++) {
objects[divIndex-1].style.display = "block";
descripts[divIndex-1].style.display = "block";
thumbs[divIndex-1].className += " active";
}
}
});
</script>
<div class="module">
<div class="content">LOREM IPSUM 1</div>
<div class="wrapper">
<div class="content">LOREM IPSUM 1</div>
<div class="object">o1</div>
<div class="object">o2</div>
<div class="object">o3</div>
<div class="descript">d1</div>
<div class="descript">d2</div>
<div class="descript">d3</div>
<div class="thumb" onclick="currentDiv(1)">t1</div>
<div class="thumb" onclick="currentDiv(2)">t2</div>
<div class="thumb" onclick="currentDiv(3)">t3</div>
<a class="prev" onclick="plusDivs(-1)">X</a>
<a class="next" onclick="plusDivs(1)">X</a>
</div>
</div>
<div class="module">
<div class="content">LOREM IPSUM 2</div>
<div class="wrapper">
<div class="content">LOREM IPSUM 2</div>
<div class="object">o4</div>
<div class="object">o5</div>
<div class="object">o6</div>
<div class="descript">d4</div>
<div class="descript">d5</div>
<div class="descript">d6</div>
<div class="thumb" onclick="currentDiv(1)">t4</div>
<div class="thumb" onclick="currentDiv(2)">t5</div>
<div class="thumb" onclick="currentDiv(3)">t6</div>
<a class="prev" onclick="plusDivs(-1)">X</a>
<a class="next" onclick="plusDivs(1)">X</a>
</div>
</div>
Is this what you are looking for? See comments inline. Also, don't use .getElementsByClassName().
// Convert the node list into an Array for the best browser compatibility with Array.forEach()
let modules = Array.prototype.slice.call(document.querySelectorAll("div[class^='module-']"));
// Loop over the modules.
// The Array.forEach() method is much simpler than manual loops because you don't have
// to maintain the loop indexer.
modules.forEach(function(module){
// Get the objects and descriptions (no arrays needed here because we're just
// going to need to use indexes against the node lists.
let objects = module.querySelectorAll("div[class='object']");
let descriptions = module.querySelectorAll("div[class='descript']");
// Clear out the objects and descriptions in the module.
// Start by getting all the objects and descriptions into an array.
let objectsDescriptions = Array.prototype.slice.call(
module.querySelectorAll("[class='object'], [class='descript']"));
// Then remove each item in the array from the document
objectsDescriptions.forEach(function(element){ element.parentNode.removeChild(element); });
// Loop the amount of times that matches the number of items in one of the arrays.
// Here, a regular counting loop makes the most sense because it's all about looping
// the correct number of times, not looping over DOM elements.
for(var i = 0; i < objects.length; i++){
// Repopulate the module with the current child elements, but in the new sequence
module.insertBefore(objects[i], module.querySelector(".thumb"));
module.insertBefore(descriptions[i], module.querySelector(".thumb"));
}
// Set up all the clickable elements to have click event handlers
module.addEventListener("click", function(evt){
// Check to see if it was a thumb or a prev/next that was clicked
if(evt.target.classList.contains("thumb")){
// Show the div that has the same index as the thumbnail that was clicked
let thumbs = Array.prototype.slice.call(evt.target.parentNode.querySelectorAll(".thumb"));
showDiv(evt.target.parentNode, thumbs.indexOf(evt.target));
} else if(evt.target.classList.contains("prev") || evt.target.classList.contains("next")){
// Show the div according to the data-offset attribute of the clicked element
showDiv(evt.target.parentNode, +evt.target.dataset.offset, true);
}
});
});
// ****************************************************************
// CODE TO SHOW DIVS
// ****************************************************************
let currentIndex = 0;
// div display
function showDiv(parent, index, nav) {
// Hide all the objects and descriptions
let items = parent.querySelectorAll(".object, .descript");
Array.prototype.slice.call(items).forEach(function(el){
el.classList.add("hidden");
});
if(nav){
currentIndex += index; // Adjust for the offset
if(currentIndex < 0){
currentIndex = 0;
} else if(currentIndex > (items.length / 2) - 1){
currentIndex = (items.length / 2) - 1;
}
// Show just the ones that are supposed to be shown
parent.querySelectorAll(".object")[currentIndex].classList.remove("hidden");
parent.querySelectorAll(".descript")[currentIndex].classList.remove("hidden");
} else {
// Show just the ones that are supposed to be shown
parent.querySelectorAll(".object")[index].classList.remove("hidden");
parent.querySelectorAll(".descript")[index].classList.remove("hidden");
}
}
.hidden { display:none; }
.thumb, .prev, .next { cursor:pointer; color:blue; }
<div class="module-1">
<div class="object">o1</div>
<div class="object">o2</div>
<div class="object">o3</div>
<div class="descript">d1</div>
<div class="descript">d2</div>
<div class="descript">d3</div>
<div class="thumb">t1</div>
<div class="thumb">t2</div>
<div class="thumb">t3</div>
<span class="prev" data-offset="-1"><</span>
<span class="next" data-offset="1">></span>
</div>
<div class="module-2">
<div class="object">o4</div>
<div class="object">o5</div>
<div class="object">o6</div>
<div class="descript">d4</div>
<div class="descript">d5</div>
<div class="descript">d6</div>
<div class="thumb">t4</div>
<div class="thumb">t5</div>
<div class="thumb">t6</div>
<span class="prev" data-offset="-1"><</span>
<span class="next" data-offset="1">></span>
</div>
I'm currently building a script to create a slider and stumbled upon an error I can't seem to solve. Basically I trying to get the width of a container and multiply it base on the number of slides inside.
Here is a snippet a of the code I'm working on. Whenever I try to use .width in order to fetch the width of a container it returns undefined on the console. I tried looking back and forth on my code but I can't seem to pinpoint the issue.
_setSliderWidth() {
this.sliderBanner = this.$el.find('.slider-banner');
this.sliderBannerWidth = this.sliderBanner.width();
console.log(this.sliderBannerWidth);
this.slides.width(this.sliderBannerWidth);
this.slidesContainer.width(this.sliderBanner.width() * this.slideCount);
}
-- -- -- -- --
'use strict';
(function($) {
/**
* Base Image Slider class
*/
class ImageSlider {
constructor(el) {
this.$el = $(el);
this._dom();
this.slideCount = this.slides.length;
this.currentSlide = 0;
this.arrows = {
prev: this.$el.find('.arrow.-prev'),
next: this.$el.find('.arrow.-next')
};
// image formatting and detection
this.$el.find('img').each(function(e, el) {
let $img = $(el);
if ($img.height() > $img.width())
$img.addClass('-portrait');
});
this._setSliderWidth();
}
_dom() {
this.slides = this.$el.find('.slides');
this.slidesContainer = this.$el.find('.slider-items');
}
init() {
this._bind();
this._evaluatePosition();
}
_bind() {
this.arrows.next.on('click', this._nextSlide.bind(this));
this.arrows.prev.on('click', this._prevSlide.bind(this));
}
_nextSlide() {
this.currentSlide++;
this._moveSlide();
}
_prevSlide() {
this.currentSlide--;
this._moveSlide();
}
_setSliderWidth() {
this.sliderBanner = this.$el.find('.slider-banner');
this.sliderBannerWidth = this.sliderBanner.width();
console.log(this.sliderBannerWidth);
this.slides.width(this.sliderBannerWidth);
this.slidesContainer.width(this.sliderBanner.width() * this.slideCount);
}
_moveSlide() {
// set the min and max range
if (this.currentSlide < 0) this.currentSlide = 0;
if (this.currentSlide > this.slideCount - 1) this.currentSlide = this.slideCount - 1;
this._evaluatePosition();
this._move();
}
_move() {
let position = this.currentSlide * -100;
this.slidesContainer.css({
transform: 'translate(' + position + '%, 0)'
});
}
_evaluatePosition() {
this.arrows.prev.toggleClass('-hide', (this.currentSlide === 0));
this.arrows.next.toggleClass('-hide', (this.currentSlide === this.slideCount - 1));
}
}
$(document).ready(function() {
//--------------------------------------------------
// Image Slider
let $imageSliders = $('.image-slider');
$imageSliders.each(function(e, el) {
let imageSlider = new ImageSlider(el);
imageSlider.init();
});
//--------------------------------------------------
// Slider Banner
let $bannerSliders = $('.slider-banner');
$bannerSliders.each(function(e, el) {
let bannerSlider = new ImageSlider(el);
bannerSlider.init();
});
});
})(jQuery);
HTML
<div class="slider-banner -alternate">
<span href="#" class="arrow -prev -hide"></span>
<span href="#" class="arrow -next"></span>
<div class="slider-items">
<div class="slides">
<div class="image" style="background-image:url(/iom/sites/default/files/2018-07/partnerships-2_0.jpg)">
<div class="banner-detail">
<div class="article-detail">
<div class="timestamp">
page
</div>
<h2 class="title">
Migrant Integration
</h2>
<div class="mini-caption">
IOM supports policies and strategies that promote the social, economic and cultural inclusion of migrants within existing legal frameworks in countries of destination.
</div>
More Details
</div>
</div>
</div>
</div>
<div class="slides">
<div class="image" style="background-image:url(/iom/sites/default/files/2018-07/definitional-issues_1.jpg)">
<div class="banner-detail">
<div class="article-detail">
<div class="timestamp">
page
</div>
<h2 class="title">
Forum on Migration, Trade and the Global Economy
</h2>
<div class="mini-caption">
IOM, together with partners ICTSD and FundanciĆ³n Foro del Sur will host the Forum on Migration, Trade & the Global Economy in Buenos Aires on 14 December.
</div>
More Details
</div>
</div>
</div>
</div>
<div class="slides">
<div class="image" style="background-image:url(/iom/sites/default/files/2018-07/identity-management_0.jpg)">
<div class="banner-detail">
<div class="article-detail">
<div class="timestamp">
page
</div>
<h2 class="title">
Comparative Research on the Assisted Voluntary Return and Reintegration of Migrants
</h2>
<div class="mini-caption">
Assisted Voluntary Return and Reintegration (AVRR) is an indispensable part of a comprehensive approach to migration management aiming at orderly and humane return and reintegration of migrants.
</div>
More Details
</div>
</div>
</div>
</div>
</div>
</div>
It seems from your screenshots and code that the this. sliderBanner object does NOT return a DOM object and thus the .width() would be undefined.
To resolve you can:
1) Retrieve the DOM object through a weird method of this.sliderBanner.prevObject. More information in this thread: What is prevObject and why is my selector returning that?
The main problem is that the .find from the $el object can't doesn't have the slider banner object within it's DOM, so...
2) Try using this.sliderBanner = $(".slider banner") to select the banner from the document object instead
I have the following html
<div id="main">
<aside id="list"><p>sometext</p></aside>
<aside id="list-2"><p>sometext</p></aside>
<aside id="list-3"><p>sometext</p></aside>
<aside id="list-4"><p>sometext</p></aside>
</div>
I want to use javascript to make it look like :
<div id="main">
<aside id="list"><p>sometext</p></aside>
<div id="wrap">
<aside id="list-2"><p>sometext</p></aside>
<aside id="list-3"><p>sometext</p></aside>
<aside id="list-4"><p>sometext</p></aside>
</div>
</div>
I have tried insertAdjacentHTML and innerHTML methods :
widgets = document.getElementById('main');
widgets.innerHTML = "<div class='box-class'>" + widgets.innerHTML + "</div>";
But this adds wrapper over "list" too.
There are two big problems with the code you said you tried (three if widgets isn't declared anywhere):
widgets = document.getElementById('main');
widgets.innerHTML = "<div class='box-class'>" + widgets.innerHTML + "</div>";
Using strings means the browser has to go through the elements, build an HTML string for them, and return that string to JavaScript; then when you assign to innerHTML it has to destroy the elements that are already there and build new replacement ones by parsing the HTML string. This will wipe out any event handlers or similar attached to the elements. (Of course, if there aren't any, it doesn't matter much.)
That wraps all of the children, not just the ones after the first child.
(It also wraps them in <div class='box-class'>, not <div id="wrap">, but...)
On all modern browsers, elements have a children list you can use for this. Then just create a wrapper, move the children other than the first into it, and append it.
var main = document.getElementById("main");
var wrapper = document.createElement("div");
wrapper.id = "wrap";
while (main.children.length > 1) {
// Note: Appending the element to a new parent removes it from its original
// parent, so `main.children.length` will decrease by 1
wrapper.appendChild(main.children[1]);
}
main.appendChild(wrapper);
Example:
var main = document.getElementById("main");
var wrapper = document.createElement("div");
wrapper.id = "wrap";
while (main.children.length > 1) {
wrapper.appendChild(main.children[1]);
}
main.appendChild(wrapper);
#wrap {
border: 1px solid blue;
}
<div id="main">
<aside id="list">
<p>sometext</p>
</aside>
<aside id="list-2">
<p>sometext</p>
</aside>
<aside id="list-3">
<p>sometext</p>
</aside>
<aside id="list-4">
<p>sometext</p>
</aside>
</div>
Side note: In your markup, you have </div> where you want </aside>.
I am having problems with this function not running correctly... it only makes the last Element have the box appear.
NOTES: <aside> is position: fixed; and I do know this is not a "Proper" use of <article> tags, but it helps me to differentiate them for now.
HTML:
<aside class="character">
<div class="personHolder">
<div class="person"></div>
</div>
<div class="arrow_box"></div>
</aside>
<main class="main">
<section class="sections" id="Home">
<article class="article1">
<h1 class="sectionHeaders">Home</h1>
</article>
</section>
<section class="sections" id="About">
<article class="article2">
<h1 class="sectionHeaders">About Me</h1>
</article>
</section>
<section class="sections" id="Projects">
<article class="article3">
<h1 class="sectionHeaders">Projects</h1>
</article>
</section>
<section class="sections" id="Contact">
<article class="article3">
<h1 class="sectionHeaders">Contact Me</h1>
</article>
</section>
</main>
JavaScript/JQuery:
function checkElement() {
var article1 = $(".article1");
var article2 = $(".article2");
var article3 = $(".article3");
var article4 = $(".article4");
var arrowTop = 170;
var arrowBottom = 258;
var articles = [article1, article2, article3, article4];
$.each(articles, function(index, value) {
if(value.offset().top < arrowTop &&
value.offset().top + value.height() > arrowBottom) {
$(".arrow_box").show();
} else {
$(".arrow_box").hide();
}
});
}
The following is the best thing I can do to a Fiddle, as I cannot make the fiddle work correctly... (Sorry)
Free Website Host
I have tried the below before as well.
$("article").each(function() {
if(this.offset().top < arrowTop &&
this.offset().top +
this.height() > arrowBottom) {
$(".arrow_box").show();
} else {
$(".arrow_box").hide();
}
});
FINAL SOLUTION:
var showing = false;
$("article").each(function() {
if (showing) return;
if($(this).offset().top < arrowTop &&
$(this).offset().top +
$(this).height() > arrowBottom) {
$(".arrow_box").show();
showing = true;
} else {
$(".arrow_box").hide();
}
});
It seems like you are saying that each article has its own arrow box.
In your function, you will check the offset of all articles, but the $(".arrow_box") selector will be the same for all articles, so you will hide/show it only depending on the last articles offset.
I dont know your HTML tree, but try to change the selector to something like
value.closest(".arrow_box").show();
Update
You want to cancel the each() once you have found an article in range. This can be done like this for instance:
var showing = false;
$("article").each(function() {
if (showing) return;
if(this.offset().top < arrowTop &&
this.offset().top +
this.height() > arrowBottom) {
$(".arrow_box").show();
showing = true;
} else {
$(".arrow_box").hide();
}
});