jquery .each() only doing last element - javascript

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();
}
});

Related

Iterate through multiple parent divs (with same class) to access the child divs inside (same class) using Javascript

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>

JS next tab jump section

I have a simple next and previous tab to move between "section". When you click the next tab the page moved down and skips the next "section" and goes the one after.
var $sec = $("section");
$(".prev, .next").click(function() {
var y = $sec.filter(function(i, el) {
return el.getBoundingClientRect().top > 0;
})[$(this).hasClass("next") ? "next" : "prev"]("section").offset().top;
$("html, body").stop().animate({
scrollTop: y
});
});
<div data-role="page" id="main">
<section>
<div id="home-section" class="section">
<img src="images/welcome-homepage.jpg" />
</div>
</section>
<section>
<div id="about-section" class="section">
<img src="images/about-us.jpg" />
</div>
</section>
<section>
<div id="service-charter-section" class="section">
<img src="images/service-charter.jpg" />
</div>
</section>
<section>
<div id="testionials-section" class="section">
<img src="images/about-us.jpg" />
</div>
</section>
</div>
This should work. It does not account for current scroll position, i.e. if you scroll halfway down and hit next, it will continue in series from the last item clicked through. It just makes an array of all sections positions and hops through that as you click prev / next.
jQuery(document).ready(function($) {
var sectionPosition = 0;
var scrollPositions = []
function scroll(y) {
$('html').stop().animate({
scrollTop: y
});
}
$("section").each(function(i, el) {
scrollPositions.push(parseInt($(el).offset()['top']))
})
$(".prev, .next").click(function() {
if ($(this).hasClass('next')) {
scroll(scrollPositions[sectionPosition + 1]);
if (sectionPosition < scrollPositions.length) {
sectionPosition++;
}
} else if (sectionPosition > 0) {
scroll(scrollPositions[sectionPosition - 1]);
sectionPosition--;
}
});
});

Using .width() returns undefined

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

Count and display most recent blog posts in javascript

What is the javascript in order to only display posts 3 & 4 in order???
Also I need it be dynamic so if I put a 5th post it will only display 4th and 5th posts... I was thinking about something like a date function or a simple incrementor but can't seem to figure it out. I'm new to javascript and have been trying different things but no avail... Thanks in advance...
<!DOCTYPE html>
<html>
<body>
<div id="posts-div">
<h1 class="post-title">post4</h1>
<p class="post">post4</p>
</div>
<div id="posts-div">
<h1 class="post-title">post3</h1>
<p class="post">post3</p>
</div>
<div id="posts-div">
<h1 class="post-title">post2</h1>
<p class="post">post2</p>
</div>
<div id="posts-div">
<h1 class="post-title">post1</h1>
<p class="post">post1</p>
</div>
<script>
// ???
</script>
</body>
</html>
You dont need script for that. You can do it with CSS.. I have changed your html little bit (made posts-div class in html).
.posts-div{
display:none;
}
.posts-div:nth-child(-n+2) {
display:block;
}
<!DOCTYPE html>
<html>
<body>
<div class="posts-div">
<h1 class="post-title">post5</h1>
<p class="post">post5</p>
</div>
<div class="posts-div">
<h1 class="post-title">post4</h1>
<p class="post">post4</p>
</div>
<div class="posts-div">
<h1 class="post-title">post3</h1>
<p class="post">post3</p>
</div>
<div class="posts-div">
<h1 class="post-title">post2</h1>
<p class="post">post2</p>
</div>
<div class="posts-div">
<h1 class="post-title">post1</h1>
<p class="post">post1</p>
</div>
<script>
// ???
</script>
</body>
</html>
You can test it on JSfiddle as well.. https://jsfiddle.net/nimittshah/b5eL3ykx/6/
$('.posts-div:gt(1)').hide()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<body>
<div class="posts-div">
<h1 class="post-title">post4</h1>
<p class="post">post4</p>
</div>
<div class="posts-div">
<h1 class="post-title">post3</h1>
<p class="post">post3</p>
</div>
<div class="posts-div">
<h1 class="post-title">post2</h1>
<p class="post">post2</p>
</div>
<div class="posts-div">
<h1 class="post-title">post1</h1>
<p class="post">post1</p>
</div>
</body>
Try this:
<script>
document.addEventListener("DOMContentLoaded", function () {
var allPosts = document.querySelectorAll(".posts-div");
// This is the number of posts you want displayed
var numberOfPostsToShow = 2;
for (var i = 0; i < allPosts.length; i++) {
if(i > numberOfPostsToShow - 1) {
allPosts[i].style.display = "none";
}
}
});
</script>
This way you will choose how many posts you want to be shown with the numberOfPostsToShow variable.
Let me know if this worked. Regards.
The way I interpreted your question, you need a way to:
show only the first n elements;
add new elements to the top of the list of posts, dynamically;
when you add them, update the visible elements.
Assuming a slightly modified version of your code, which corrects the id/class issue and adds a container for all the posts (this time with a proper id):
<!DOCTYPE html>
<html>
<body>
<div id="posts-container">
<div class="posts-div">
<h1 class="post-title">post4</h1>
<p class="post">post4</p>
</div>
<div class="posts-div">
<h1 class="post-title">post3</h1>
<p class="post">post3</p>
</div>
<div class="posts-div">
<h1 class="post-title">post2</h1>
<p class="post">post2</p>
</div>
<div class="posts-div">
<h1 class="post-title">post1</h1>
<p class="post">post1</p>
</div>
</div>
<script>
// ???
</script>
</body>
</html>
this code will do the trick and manage both the addition and the updates to the visibility of the posts:
function showOnly(visible, query){
var elements = document.querySelectorAll(query);
for (var i = 0; i < elements.length; i++){
if (i < visible - 1){
elements[i].style.display = 'block';
} else {
elements[i].style.display = 'none';
}
}
}
function publishPost(element, visible){
showOnly(visible, '#posts-container .posts-div')
var elements = document.querySelectorAll('#posts-container .posts-div');
element.style.display = 'block';
if (elements.length > 0) {
document.querySelector('#posts-container').insertBefore(element, elements[0]);
} else {
document.querySelector('#posts-container').appendChild(element);
}
}
The showOnly function (to be called with the number of elements to be shown and the string that identifies the elements with querySelectorAll) will only make visible the first n elements identified by the string. You can use it independently of the rest of the code if needed.
The publishPost function, on the other hand, is strictly dependent on the modified html above (to use it elsewhere you will need to adjust the strings fed to querySelector and querySelectorAll). It takes the element to be published as the first argument, the number of elements that need to be visible as the second. Then it updates the list of posts prepending the new one to it, and it also updates which posts are visible.
This is a code sample that uses it:
var elDiv = document.createElement('div');
var elH1 = document.createElement('h1');
var elP = document.createElement('p');
elDiv.classList = 'posts-div';
elH1.classList = 'post-title';
elP.classList = 'post';
elH1.innerText = 'some title';
elP.innerText = 'some text for the post';
elDiv.appendChild(elH1).appendChild(elP);
publishPost(elDiv, 2);
showOnly
This function starts by getting a list of the elements whose visibility must be managed:
var elements = document.querySelectorAll(query);
then it loops through the list and examines each element:
for (var i = 0; i < elements.length; i++){
if it has to be visible, it sets the style.display property to 'block':
if (i < visible){
elements[i].style.display = 'block';
otherwise it sets it to 'hidden':
else {
elements[i].style.display = 'none';
publishPost
This function starts by showing only n-1 elements (because it will need to add a new, visible element to the top of the list):
showOnly(visible - 1, '#posts-container .posts-div')
then it retrieve the current posts:
var elements = document.querySelector('#posts-container .posts-div');
it makes the new element visible:
element.style.display = 'block';
finally, it adds the element to the top of the list (the different syntax depends on wether the list is empty):
if (elements.length > 0) {
document.querySelector('#posts-container').insertBefore(element, elements[0]);
} else {
document.querySelector('#posts-container').appendChild(element);
}

Add class (odd and even) in html via javascript?

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++;
}
}

Categories

Resources