How to offset top bar height dynamically when scrolling down - javascript

I am trying to offset an announcement bar when scrolling down, taking into consideration that the height of the bar is more important on smaller devices.
Here's an example of what I want to achieve : https://8kflexwarm.com/
So I ended up with this piece of code, which is working, but I feel like it is not optimized, not clean code. I figure there must be a way to offset $('.announcement-bar') instead of doing it manually with window size.
Also, why is this code not working when I refresh the screen and I'm not on top of the page ?
Is there a way to improve this code without using a library ?
if($(window).width() >= 686){
var a = $(".site-header").offset().top;
function scrollListener(){
if($(document).scrollTop() > a)
{
$('.site-header').css({"margin-top":"-40px"});
$('.site-header').css({"transition":"0.4s"});
} else {
$('.site-header').css({"margin-top":"0px"});
$('.site-header').css({"transition":"0.4s"});
}
};
$(document).scroll(scrollListener);
scrollListener();
} else if($(window).width() >= 370) {
var a = $(".site-header").offset().top;
function scrollListener(){
if($(document).scrollTop() > a)
{
$('.site-header').css({"margin-top":"-65px"});
$('.site-header').css({"transition":"0.4s"});
} else {
$('.site-header').css({"margin-top":"0px"});
$('.site-header').css({"transition":"0.4s"});
}
};
$(document).scroll(scrollListener);
scrollListener();
} else {
var a = $(".site-header").offset().top;
function scrollListener(){
if($(document).scrollTop() > a)
{
$('.site-header').css({"margin-top":"-90px"});
$('.site-header').css({"transition":"0.4s"});
} else {
$('.site-header').css({"margin-top":"0px"});
$('.site-header').css({"transition":"0.4s"});
}
};
$(document).scroll(scrollListener);
scrollListener();
};

Please provide a codePen, it makes it easier to help you with your question.
I came up with this untested piece of javascript:
var myApp = (function(app) {
const $elements = {
siteHeader = null,
}
function setPosition() {
const scrollTop = $(document).scrollTop()
const offsetTop = $elements.siteHeader.offset().top
if(scrollTop > offsetTop){
$elements.siteHeader.css({'margin-top':`${$elements.siteHeader.height() * -1}px`})
} else {
$elements.siteHeader.css({'margin-top':'0px'})
}
}
function initialize() {
// Wait for all elements to be created
$(function() {
$elements.siteHeader = $('.site-header')
setPosition()
$(document).scroll(setPosition)
$(document).resize(setPosition)
})
}
initialize()
return app;
})(myApp || {})

Related

Angular specific way to determine scrolling direction

Is there any angular specific way to determine if a user is scrolling upwards or downwards?
I came across solutions which all were either about jQuery or pure JavaScript.
I tried below JSFiddle but not getting it right, it always shows scrolling up.
JSFiddle Demo
Here's how I tried it:
this.currentPosition = window.pageYOffset;
onContentScrolled(e) {
let scroll = window.pageYOffset;
if (scroll > this.currentPosition) {
console.log('scrollDown');
} else {
console.log('scrollUp');
}
this.currentPosition = scroll;
}
The fiddle works all fine. But I want to know the correct way to implement in angular component.
It only outputs "scrollUp" every time. Can anyone tell me what I am doing wrong here. I think it's with the global currentPosition variable but don't know how to proceed further.
It depends on where you want to add the listener (to a specific component, to the document, to the body, etc). A silver bullet is to use a regular event listener. As an example let's listen to scrolling events on the document. You can inject the document in the constructor (just in case you want to use something like SSR in the future):
Stackblitz demo
constructor(#Inject(DOCUMENT) private _document: Document) {
this._document.addEventListener('scroll', this.onContentScrolled);
}
ngOnDestroy() {
this._document.removeEventListener('scroll', this.onContentScrolled);
}
onContentScrolled = (e) => {
let scroll = window.pageYOffset;
if (scroll > this.currentPosition) {
console.log('scrollDown');
} else {
console.log('scrollUp');
}
this.currentPosition = scroll;
}
If you want to do the same thing with the scrollbar contained within a component, you can use the #HostListener('scroll') to decorate the listener method inside the component you want to listen to scrolling events.
Stackblitz demo
#HostListener("scroll", ['$event.target'])
onContentScrolled(e: HTMLElement) {
let scroll = e.scrollTop;
if (scroll > this.currentPosition) {
console.log("scrollDown");
} else {
console.log("scrollUp");
}
this.currentPosition = scroll;
}
my observable version of it: StackBlitz
Maybe I have lot more complex structure in my app, which includes dynamic content from various components, so I tried below and it worked seamlessly!
private scrollChangeCallback: () => void;
currentPosition: any;
startPosition: number;
ngAfterViewInit() {
this.scrollChangeCallback = () => this.onContentScrolled(event);
window.addEventListener('scroll', this.scrollChangeCallback, true);
}
onContentScrolled(e) {
this.startPosition = e.srcElement.scrollTop;
let scroll = e.srcElement.scrollTop;
if (scroll > this.currentPosition) {
this.showButton = false;
} else {
this.showButton = true;
}
this.currentPosition = scroll;
}
ngOnDestroy() {
window.removeEventListener('scroll', this.scrollChangeCallback, true);
}
e.srcElement works like a charm!
And thanks for all solutions above! They weren't wrong just didn't fit to my app
I know this is pretty late, but here's what worked for me in angular 9.
Typescript
lastScrollTop = 0
scrollHandler(event) {
let currentScrollTop = event.currentTarget.scrollTop
if (currentScrollTop > this.lastScrollTop) this.handleScrollDown()
else this.handleScrollUp()
this.lastScrollTop = currentScrollTop
}
HTML
<div class="container border" (scroll)="scrollHandler($event)">
</div>
this work like a charm for me using Angular 13
----------------------------HTML CODE------------------------------------------------
<div class="list"
(scroll)="onScroll($event)">
here you list or content to paginate /scroll
</div>
-----------------------------TS CODE---------------------------------------------------
startPosition: number = 0; //global variable
onScroll(event: any) {
if (
this.startPosition <=
event.target.offsetHeight + event.target.scrollTop + 50
) {
console.log('scrolled down!!', event);
} else {
console.log('scrolled up!!', event);
}
this.startPosition =
event.target.offsetHeight + event.target.scrollTop + 50;
}
TO DO refactor ,Hope this helps you ,have a nice coding ^^
you can use:
lastScrollTop = 0;
hold = false;
constructor(#Inject(DOCUMENT) private _document: Document) {
this._document.addEventListener('scroll', this.onContentScrolled);
}
onContentScrolled(e) {
const windowHeight = window.innerHeight;
const st = document.documentElement.scrollTop;
if (document.documentElement.scrollTop < 200) {
this.mainSectionOneIndex = 0;
}
if (st > this.lastScrollTop) {
if (this.hold === false) {
this.hold = true;
console.log('down');
setTimeout(() => {
this.hold = false;
}, 500);
}
} else {
if (this.hold === false) {
this.hold = true;
console.log('up');
setTimeout(() => {
this.hold = false;
}, 500);
}
}
this.lastScrollTop = st <= 0 ? 0 : st;
}

How to shorten these jQuery functions?

I have these 3 jQuery functions that basically do the same thing, but they are a tiny bit different (width of window, and class that is removed/toggled)
I need the functionality of all these 3 functions, but want to somehow combine them/shorten them into one function. I've tried to but my code keeps breaking
Can anyone help to shorten them?
Here are the 3 functions
jQuery(document).ready(function($) {
$('.exampleimg').click(function() {
$('.about').hide(600);
if (($(window).width() > 670) && ($(this).hasClass('exampleimgopen'))) {
$(this).removeClass('exampleimgopen');
} else if ($(window).width() > 670) {
$('.exampleimg').removeClass('exampleimgopen');
$(this).addClass('exampleimgopen');
}
});
});
jQuery(document).ready(function($) {
$('.exampleimg').click(function() {
$('.about').hide(600);
if (($(window).width() < 670) && ($(this).hasClass('exampleimgopen2'))) {
$(this).removeClass('exampleimgopen2');
} else if ($(window).width() < 670) {
$('.exampleimg').removeClass('exampleimgopen2');
$(this).addClass('exampleimgopen2');
}
});
});
jQuery(document).ready(function($) {
$('.exampleimg').click(function() {
$('.about').hide(600);
if (($(window).width() < 540) && ($(this).hasClass('exampleimgopen3'))) {
$(this).removeClass('exampleimgopen3');
} else if ($(window).width() < 540) {
$('.exampleimg').removeClass('exampleimgopen3');
$(this).addClass('exampleimgopen3');
}
});
});
I need the functionality of all these 3 functions, but want to somehow combine them/shorten them into one function.
Generally, a good approach when refactoring similar functions is to create a single factory function that will take your variable data as arguments and return a function that has access to that data via its closure.
function myFactory(conditionFunc, windowWidth, cssClass) {
return function() {
$('.about').hide(600);
var windowCondition = conditionFunc($(window).width(), windowWidth);
if (windowCondition && ($(this).hasClass(cssClass))) {
$(this).removeClass(cssClass);
} else if (windowCondition) {
$('.exampleimg').removeClass(cssClass);
$(this).addClass(cssClass);
}
}
}
Then you can call this function 3 times to build your functions:
// helper methods that will perform '<' or '>' on window width
var lessThan = function(a, b) { return a < b; };
var greaterThan = function(a, b) { return a > b; };
var func1 = myFactory(greaterThan, 670, 'exampleimgopen');
var func2 = myFactory(lessThan, 670, 'exampleimgopen2');
var func3 = myFactory(lessThan, 540, 'exampleimgopen3');
Which you can then pass each into their corresponding listeners.
$('.exampleimg').click(func1);
$('.exampleimg').click(func2);
$('.exampleimg').click(func3);
The advantage of doing things this way is that you only write a single function which then creates different versions of your listener callback functions, based on the data you give to it.
I think is maybe closer to what you wanted, although it's unclear what you wanted to have happen when the width was < 540. Might want to use if .. then ... else instead
jQuery(document).ready(function($) {
$('.exampleimg').click(function() {
var width = $(window).width();
var classes = [];
if (width < 540) {
classes.push('exampleimgopen3');
}
if ($(window).width() < 670) {
classes.push('exampleimgopen2');
}
if ($(window).width() >= 670) {
classes.push('exampleimgopen');
}
classes.forEach(function(class) {
$('.exampleimg').removeClass(class);
if (!$(this).hasClass(class)) {
$(this).addClass(class);
}
});
});
});

JavaScript - Hide an element when another element is fully inside the viewport

I want to hide .isi-jumo-link when .indication is fully visible in the viewport. Currently it only disappears once .indication is at the top of the viewport.
User needs to scroll from the top and once .indication is fully in view then .isi-jump-link will disappear.
$(window).on('scroll', function () {
if ($(this).scrollTop() >= $('.indication').offset().top) {
$('.isi-jump-link').hide();
}
else {
$('.isi-jump-link').show();
}
});
Just to note... using a fixed scrollTop in my case will not work.
You could check if the both the top and the bottom of your indication is within the viewport:
$(window).on('scroll', function () {
var bottomIsVisible = $(this).scrollTop() + $(this).height() >= $('.indication').offset().top + $('.indication').height();
var topIsVisible = var topIsVisible = $(this).scrollTop() <= $('.indication').offset().top;
if (bottomIsVisible && topIsVisible) {
$('.isi-jump-link').hide();
}
else {
$('.isi-jump-link').show();
}
});
You need to add the height of the .indication <div> also to the equation:
$(window).on('scroll', function () {
if ($(this).scrollTop() >= ($('.indication').offset().top + $('.indication).height())) {
$('.isi-jump-link').hide();
}
else {
$('.isi-jump-link').show();
}
});

Get static value from variable (scroll function)

I have a "follow scroll" function, but I want it to turn off when it returns to a certain point. My code is as follows:
scrollSidebar: function(scroll) {
var elemPos = $('#bestvideos-2').offset().top,
scroll2 = scroll;
if(scroll2 >= elemPos) {
$('#bestvideos-2').animate({
'margin-top':(scroll - 315)+'px'
},0);
} else {
$('#bestvideos-2').css('margin-top','0');
}
}
$(window).scroll(function() {
var scrollHeight = $(window).scrollTop();
Scroll.scrollSidebar(scrollHeight);
})
The problem is - every time I get up, it goes way up, not following scroll. What I'm thinking is storing a variable elemPos somewhere and keep it static (now it's changing each time I scroll).
What can I do with this?
Pass the value to the scrollSidebar function - make sure that the var elemPos = $('#bestvideos-2').offset().top is executed on dom ready
scrollSidebar: function (elemPos, scroll) {
var scroll2 = scroll;
if (scroll2 >= elemPos) {
$('#bestvideos-2').animate({
'margin-top': (scroll - 315) + 'px'
}, 0);
} else {
$('#bestvideos-2').css('margin-top', '0');
}
}
var elemPos = $('#bestvideos-2').offset().top
$(window).scroll(function () {
var scrollHeight = $(window).scrollTop();
Scroll.scrollSidebar(elemPos, scrollHeight);
})

Javascript parameter help needed

I've this function:
$(function() {
$(window).scroll(function(e) {
var sl = $(this).scrollLeft();
var lbl = $("#waypoints");
if (sl > 0) {
lbl.show('');
}
else {
lbl.hide();
}
if (sl > 750) {
lbl.html("<p>now passed 750</p>");
}
if (sl > 1000) {
lbl.html("<p>now passed 1000</p>");
}
});
});
Which work when i scroll the browser window. But I need to adjust it, so that it is set for a div (#main) which scrolls inside another div (.content) which has a fixed width and css overflow enabled.
I've tried $(#main).scroll(function(e) { no joy ...
thanks for reading, any help would be awesome.
Try to change to class:
$(function() {
$('.content').scroll(function(e) {
var sl = $(this).scrollLeft();
var lbl = $("#waypoints");
if (sl > 0) {
lbl.show();
}
else {
lbl.hide();
}
if (sl > 750) {
lbl.html("<p>now passed 750</p>");
}
if (sl > 1000) {
lbl.html("<p>now passed 1000</p>");
}
});
});​
Here's a JSFiddle
I don't see what's the problem here - it's working (I mean the scroll events are fired, as you can see by scrollLeft value that is changing) http://jsfiddle.net/DTKeX/2/

Categories