Make sidebar stop at a certain div - javascript

I am trying to get the sidebar to stop below the pink categories bar 20px above the Popular Posts div. Currently, the code below stops the sidebar at the very top div in the sidebar. Is there a way I can alter this code so it stops at the last div (Popular Posts) instead?
JavaScript
/*cache jQueries for performance*/
var
$archiveTitle = jQuery ("#archive-title");
$content = jQuery ("#content"),
$categoriesBar = jQuery ("#categories-bar"),
$loadMore = jQuery ("#load-more"),
$footer = jQuery ("#footer-menu"),
$sidebar = jQuery ("#sidebar"),
$window = jQuery (window),
doSidebarAdjust = false; // only do sidebar adjustments if the DOM is stable
function isMasonryPage () {
return $loadMore.length > 0;
}
function isMasonryStable () {
return $loadMore.hasClass ("disabled");
}
function isSidebarAbove (threshold) {
return $window.scrollTop () >= threshold;
}
function isSidebarBelowFooter () {
var
categoriesBottom = $categoriesBar.offset().top + $categoriesBar.height(),
sidebarHeight = $sidebar.height (),
footerTop = $footer.offset().top;
return categoriesBottom + sidebarHeight + 50 >= footerTop;
}
function canAdjustSidebar () {
/* Determine if there's room to adjust sidebar */
var
archiveTitleHeight = $archiveTitle.length === 1 ?
$archiveTitle.outerHeight() : 0;
answer = $content.height () - 50 >
($sidebar.outerHeight () + archiveTitleHeight);
return answer;
}
function adjustSidebar (threshold) {
if (isMasonryPage ()) { // can lock to top
if (isMasonryStable ()) { // can lock to top or bottom
if (isSidebarBelowFooter ()) {
$sidebar.removeClass ("fixed").addClass ("bottom");
} else if (isSidebarAbove (threshold)) {
$sidebar.addClass ("fixed").removeClass ("bottom");
} else {
$sidebar.removeClass ("fixed");
}
} else { // masonry not stable but can lock to top
if (isSidebarAbove (threshold)) {
$sidebar.addClass ("fixed");
} else {
$sidebar.removeClass ("fixed");
}
}
} else if (canAdjustSidebar ()) { // not a masonry page but sidebar adjustable
if (isSidebarBelowFooter ()) {
$sidebar.removeClass ("fixed").addClass ("bottom");
} else if (isSidebarAbove (threshold)) {
$sidebar.addClass ("fixed").removeClass ("bottom");
} else {
$sidebar.removeClass ("fixed bottom");
}
}
}
if (jQuery(document.body).hasClass("home")) {
jQuery (window).scroll(function () { adjustSidebar (654); });
} else if (jQuery(document.body).hasClass("single") || jQuery(document.body).hasClass("page")) {
jQuery (window).scroll(function () { adjustSidebar (20); });
} else {
jQuery (window).scroll(function () { adjustSidebar (183); });
}
</script>
HTML
<div id="wrapper">
<div id="container">
<div id="content">
<div id="sidebar"></div>
<div id="masonry"></div>
</div>
</div>
</div>
<div id="footer"></div>
CSS
#sidebar {
margin: 0;
right: 0;
width: 220px;
}
#sidebar.fixed {
margin-left: 720px;
position: fixed;
right: auto;
top: 173px;
}
#sidebar.bottom {
bottom: 0;
top: auto;
position: absolute;
}

Did you try to change the position fixed to relative of the sidebar?

Related

After 2 images slided in right side, next page is blank

I want to build a slider with buttons and I can`t figure out how should I do it.
When I hit the right button it slides 2 pages then show a blank, and thinking the problem is on the arrowRight function.
If anyone knows why my code isn't working please explain to me.
//FOR SLIDER
let sliderImages = document.querySelectorAll(".slide"),
arrowLeft = document.querySelector("#arrow-left"),
arrowRight = document.querySelector("#arrow-right"),
current = 0;
function reset() {
for (let i = 0; i < sliderImages.length; i++) {
sliderImages[i].style.display = 'none';
}
}
function startSlide() {
reset();
sliderImages[0].style.display = 'block';
}
startSlide();
function slideLeft() {
reset();
sliderImages[current - 1].style.display = 'block';
current--
}
arrowLeft.addEventListener('click', function () {
if (current === 0) {
current = sliderImages.length;
}
slideLeft();
})
function slideRight() {
reset();
sliderImages[current + 1].style.display = 'block';
current++
}
arrowRight.addEventListener('click', function () {
if (current === sliderImages.length) {
current = 0;
}
slideRight();
})
I don't think your method is perfect for that task. Usually it's two containers - one for visible part, other is full of images. It'll look something like this:
const sliderImages = document.querySelectorAll(".slide"),
arrowLeft = document.querySelector("#arrow-left"),
arrowRight = document.querySelector("#arrow-right"),
container = document.querySelector(".container");
document.addEventListener("DOMContentLoaded", function() {
container.style.width = 100 * sliderImages.length + "%";
sliderImages.forEach((el) => {
el.style.width = 100 / sliderImages.length + "%";
})
startSlider()
});
function startSlider() {
let pos = 0;
arrowLeft.addEventListener("click", slideLeft)
arrowRight.addEventListener("click", slideRight)
function slideLeft() {
if(pos === 0) {
pos = sliderImages.length - 1;
} else{
pos--;
}
slide()
}
function slideRight() {
if(pos === sliderImages.length - 1) {
pos = 0;
} else {
pos++;
}
slide()
}
function slide() {
$(container).animate({left: -100 * pos + "%"}, 500)
}
}
*{
padding: 0;
margin: 0;
}
body{
width: 100%;
}
.outer-container {
width: 80%;
margin: 5px auto;
overflow: hidden;
position: relative;
}
.slide > img {
width: 100%;
}
.container {
display: flex;
position: relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer-container">
<div id="arrow-left">LEFT</div>
<div id="arrow-right">RIGHT</div>
<div class="container">
<div class="slide">
<img src="https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80">
</div>
<div class="slide">
<img src="https://cdn.pixabay.com/photo/2017/02/20/18/03/cat-2083492_960_720.jpg">
</div>
<div class="slide">
<img src="https://media.boingboing.net/wp-content/uploads/2019/02/cats.jpg">
</div>
<div class="slide">
<img src="https://cdn-images-1.medium.com/max/1600/1*mONNI1lG9VuiqovpnYqicA.jpeg">
</div>
</div>
</div>
If you just a fix for your code, here's what I've changed:
arrowRight.addEventListener('click', function () {
//Make sure that's current less than the number of images
if (current + 1 == sliderImages.length) {
current = 0;
} else {
current++;
}
slide();
})
//Moved checks and current position management from slide
//Functions to click listeners
arrowLeft.addEventListener('click', function () {
if (current === 0) {
current = sliderImages.length - 1;
} else {
current--;
}
slide();
})
//That let me merge your two function into one
//And your checks for positions was a little off
function slide(){
reset();
sliderImages[current].style.display = 'block';
}
I hope my answer helped you. If you have any questions about this code add a comment below.

clearInterval function not actually clearing

The div completes one round from left to right and right to left scrolling but gets stuck in the scrollBack() function. The program does execute the clearInterval() statement at the desired event but it doesn't actually clear the interval. What am I doing wrong?
var backint = null;
function scrollForward() {
if ($("#foo").scrollLeft() != $("#foo").width()) {
$("#foo").scrollLeft($("#foo").scrollLeft() + 1);
} else {
backint = setInterval(scrollBack, 5);
}
}
function scrollBack() {
if ($("#foo").scrollLeft() != 0) {
$("#foo").scrollLeft($("#foo").scrollLeft() - 1);
} else if ($("#foo").scrollLeft() == 0) {
clearInterval(backint);
}
}
It's better do with
.animate() as Rory McCrossan suggested because that setInterval reimplements existing thing and not necessarily better:
var foo = $("#container"),
bar = $("#foo"),
scrollSize = bar.width() - foo.width();;
function scrollForward() {
console.log('forward', foo.scrollLeft(), bar.width() - foo.width());
if (foo.scrollLeft() != scrollSize) {
foo.animate({
scrollLeft: scrollSize + 'px'
});
}
}
function scrollBack() {
console.log('back', foo.scrollLeft(), scrollSize);
if (foo.scrollLeft() === scrollSize) {
foo.animate({
scrollLeft: '0px'
});
}
}
foo.on("click", scrollForward);
foo.on("dblclick", scrollBack);
#container {
border: 1px solid #ccc;
width: 410px;
overflow-x: scroll;
height: 50px;
}
#foo {
background-color: #ccc;
width: 1300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div id="foo">Click to scroll right. Double-click to scroll left.</div>
</div>

Javascript- Execute onscroll function after a set timeout

So I am creating a page with three div's which become visible upon scrolling. The problem is that the code makes them all execute at the same time, whereas I want that their should be a time difference between the execution of each onscroll function
Relevant part of code -
HTML
<section id="section2">
<span id="one" class="classbefore">lol</span>
<span id="two" class="classbefore">lol</span>
<span id="three" class="classbefore">lol</span>
</section>
CSS
#section2 > span{
width: 25%;
height: 50%;
position: absolute;
background: #f00;
-webkit-transition: all 0.5s ease;
box-shadow: 1px #000;
}
#section2 > #one{
margin-left: 10%;
}
#section2 > #two{
margin-left: 37.5%;
}
#section2 > #three{
margin-left: 65%;
}
.classbefore{
opacity: 0;
margin-top: 18%;
}
.classafter{
opacity: 1;
margin-top: 20%;
}
Javascript
window.addEventListener('scroll', function (event) {
if (window.scrollY > 600) {
document.getElementById('one').className = "classafter";
} else {
document.getElementById('one').className = "classbefore";
}
}, true);
window.addEventListener('scroll', function (event) {
if (window.scrollY > 600) {
document.getElementById('two').className = "classafter";
} else {
document.getElementById('two').className = "classbefore";
}
}, true);
window.addEventListener('scroll', function (event) {
if (window.scrollY > 600) {
document.getElementById('three').className = "classafter";
} else {
document.getElementById('three').className = "classbefore";
}
}, true);
So, using this, all the three span become visible at the same time. Please suggest a method to give them timeout's so that one function is executed after the other.
Also, can this code be made more efficient ?
I supposed you meant to show them at different times after the same scroll.So what about this?
window.addEventListener('scroll', function (event) {
if (window.scrollY > 600) {
var waitTime = 1000; //base time for showing (in miliseconds)
setTimeout(function () {
document.getElementById('one').className = "classafter";
},waitTime);
setTimeout(function () { //We add time to the others waitings
document.getElementById('two').className = "classafter";
},waitTime + 100);
setTimeout(function () {
document.getElementById('three').className = "classafter";
},waitTime + 200);
} else {
document.getElementById('one').className = "classbefore";
document.getElementById('two').className = "classbefore";
document.getElementById('three').className = "classbefore";
}
}, true);
CODE NOT TESTED !!
Also, because it is the same event, you just need one event listeners, not 3. But that listener will change the class of the elements at diferent times
Also it would be better if you use a loop to iterate through the elements:
window.addEventListener('scroll', function (event) {
var elements = document.getElementsByName("commonName");
if (window.scrollY > 600) {
var waitTime = 1000;//base time for showing (in miliseconds)
for (var i = 0; i < elements.length; i++) {
setTimeout(function () {
elements[i].className = "classafter";
}waitTime + i * 100);
}
} else {
for (var i = 0; i < elements.length; i++) {
elements[i].className = "classafter";
}
}
}, true);
CODE NOT TESTED !!
Then it would be valid for N elements

Why isn't it possible to change max-height with % in javascript?

I'm trying to build a responsive menu, with a hamburger icon. I want the menu list to slide in and out, no jquery - pure javascript only.
HTML :
<div id="animation">
</div>
<button id="toggle">Toggle</button>
CSS :
div {
width: 300px;
height: 300px;
background-color: blue;
}
Javascript :
var but = document.getElementById('toggle');
var div = document.getElementById('animation');
var animate = function(type, callback){
var inter = -1, start = 100, end = 0;
if(type==true){
inter = 1;
start = 0;
end = 100;
}
var si = setInterval(function(){
console.log('maxheight');
div.style.maxHeight = (start + inter) + '%';
if(start == end){
clearInterval(si);
}
}, 10);
}
var hidden = false;
but.onclick = function(){
animate(hidden, function(){
hidden = (hidden == false) ? true : false;
});
}
div.style.maxHeight = "50%";
The problem is that proportional height in an element needs a fixed height on the parent, and you didn't provided any parent with a fixed height because for the maxHeight property too the % Defines the maximum height in % of the parent element.
You have to put your div in a parent container with a fixed height, this is your working code:
var but = document.getElementById('toggle');
var div = document.getElementById('animation');
var animate = function(type, callback) {
var inter = -1,
start = 100,
end = 0;
if (type) {
inter = 1;
start = 0;
end = 100;
}
var si = setInterval(function() {
console.log('maxheight');
div.style.maxHeight = (start + inter) + '%';
if (start == end) {
clearInterval(si);
}
}, 10);
}
var hidden = false;
but.onclick = function() {
animate(hidden, function() {
hidden = !hidden ;
});
}
div.style.maxHeight = "50%";
#animation {
width: 300px;
height: 300px;
background-color: blue;
}
#parent {
width: 500px;
height: 500px;
}
<div id="parent">
<div id="animation">
</div>
<button id="toggle">Toggle</button>
</div>
Note:
As stated in comments there are some statements in your JavaScript code that need to be adjusted:
if(type==true) can be written as if(type).
hidden = (hidden == false) ? true : false; can be shortened to hidden = !hidden
There seems to be a few errors with your code. I have fixed the js and added comments to what I have changed
var but = document.getElementById('toggle');
var div = document.getElementById('animation');
var animate = function (type, callback) {
var start = 100,
end = 0;
if (type) {
start = 0;
end = 100;
}
var si = setInterval(function () {
if (type) { // check whether to open or close animation
start++;
} else {
start--
}
div.style.maxHeight = start + '%';
if (start == end) {
clearInterval(si);
}
}, 10);
callback.call(this); // do the callback function
}
var hidden = false;
but.onclick = function () {
animate(hidden, function () {
hidden = !hidden; // set hidden to opposite
});
}
/*make sure parent container has a height set or max height won't work*/
html, body {
height:100%;
margin:0;
padding:0;
}
div {
width: 300px;
height: 300px;
background-color: blue;
}
<div id="animation"></div>
<button id="toggle">Toggle</button>
Example Fiddle

Add and remove class to jquery onscroll

I need to add a 'fixed' class for the header on scroll and remove it when it's scrolled back up in the following script, but not sure how best to do it:
<script>
$(document).ready(function () {
var div = $('.header');
var div2 = $('.headerPlaceholder');
var start = $(div).offset().top;
$.event.add(window, "scroll", function () {
var p = $(window).scrollTop();
$(div).css('position', ((p) > start) ? 'fixed' : 'static');
$(div).css('top', ((p) > start) ? '0px' : '');
$(div2).css('display', ((p) > start) ? 'block' : 'none');
});
});
</script>
This stop it triggering the event after each scroll down which is what happens now with the CSS specified in the script, hence I need to add a class.
CSS:
.fixed {
position: fixed;
}
Ideas appreciated.
I have translated your js to use css with the application of 1 class to the body
$(document).ready(function () {
var start = $('.header').offset().top;
$.event.add(window, "scroll", function () {
var p = $(window).scrollTop();
if( p > start ) {
$('body').addClass('fixed');
} else {
$('body').removeClass('fixed');
}
});
});
CSS
body.fixed .header {
position: fixed;
top: 0;
}
body.fixed .headerPlaceholder {
display: block;
}
The add/remove class method works great.
$(document).ready(function () {
$(window).scroll(function () {
if ($(this).scrollTop() > 100) {
$('.header').addClass("fixed");
} else {
$('.header').removeClass("fixed");
}
});
});
Here's a fiddle showing how it works: http://jsfiddle.net/gisheri/RpPEe/413/

Categories