I have a project where I try to hover an element from right to left in the window while scrolling. So when the user is scrolling down the element goes from right to left and when the user is scrolling up it goes from left to right.
I have tried to do this by adding a scroll event listener to the window and then check when this component is visible on the browser(This component is one of multiple components which are shown underneath each other in the application)
If some one knows a better way to achieve this please feel free to comment. I just thought of using the absolute positioning to move the object across the screen.
This is the code I am using at this moment but it doens't want to work. The object doesn't move and stays put.
data () {
return {
position: 0
}
},
created () {
window.addEventListener('scroll', (event) => {
this.runOnScroll()
})
},
mounted () {
const navbar = this.$refs.neonComponent
this.navbarOffset = navbar.offsetTop
},
methods: {
runOnScroll () {
if ((this.navbarOffset - 600) < window.pageYOffset) {
this.position = Math.round((window.pageYOffset - (this.navbarOffset - 600)) / 10)
this.$refs.neonText.style.right = this.position
} else {
if (this.position > 0) this.position = 0
}
// console.log(this.position)
}
}
<div ref="neonComponent" class="relative w-full min-h-screen bg-black flex justify-center items-center">
<div ref="neonText" class="absolute max-w-3xl text-center position-text">
<h2 class="text-7xl">This is an example text</h2>
</div>
</div>
Try adding a unit (px, %, vh, vw) to style.right, like so:
this.$refs.neonText.style.right = this.position + 'px';
Related
I try do a webite with different divs or for me they are sections. If I reached the top of one of these it should console log this term. If u ask, ScrollHeight is equal to 1% of the devices' screenheight.
let Point1 = false;
document.addEventListener("scroll", e=> {
if (document.documentElement.scrollTop >= 150*ScrollHeight) {
if (Point1 == false){
Point1 = true;
Point1F();
};
}
})
function Point1F() {
console.log("U've done it');
}
But its not woking for me.
Your code works, as i think the problem why you don't see your .log() is because you didn't reach it.
If scrollHeight is (as you said) "1% of the devices' screenheight", then you need html height to be ~ 3x your screen height;
document.documentElement.style.height = "300vh";
// getting 1% of screen height
const scrollHeight = screen.height / 100;
const scrollTriggerPoint = scrollHeight * 150;
let point1 = false;
document.addEventListener("scroll", (e) => {
if (document.documentElement.scrollTop >= scrollTriggerPoint) {
if (point1 == false){
point1 = true;
point1F();
};
}
});
function point1F() {
console.log("u've done it");
}
P.S.
Don't use variable's/function's names starting with a capital letter, use it on;y for constructor functions or classes.
Intersection Observer API
Using scroll position is fine when you have a single trigger point. However, when there are multiple trigger points (as the question suggests) and they are not in a consistent position on different devices, then the Intersection Observer API is a useful solution.
MDN:
Implementing intersection detection in the past involved event
handlers and loops calling methods like
Element.getBoundingClientRect() to build up the needed information for
every element affected. Since all this code runs on the main thread,
even one of these can cause performance problems. When a site is
loaded with these tests, things can get downright ugly.
You create an observer on the document or a container element and then add the elements you want to watch. And the callback is triggered when an element reaches the threshold setting.
Demo Snippet
The snippet shows how to observe different sections as they scroll in and out of view.
// create an observer on the document or container element
let observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
// code to execute when the section becomes visible
console.log("is visible: " + entry.target.id);
// uncomment to trigger only once per section
// observer.unobserve(entry.target);
}
}, {
root: document, // or container element or null
rootMargin: "0px",
threshold: 0.1
});
// add each section to the observer
document.querySelectorAll("section").forEach(target => {
observer.observe(target);
});
section {
height: 5em;
margin: 1em;
margin-bottom: 20em;
background-color: lightblue;
}
Scroll down the page to trigger the observer
<section id="section1">Section 1</section>
<section id="section2">Sectopm 2</section>
<section id="section3">Section 3</section>
<section id="section4">Section 4</section>
<section id="section5">Section 5</section>
I'm developing an application where the user logs in to their dashboard. When they log in there is a side bar to the left and main content to the right. There is a burger icon to toggle the side bar which works fine but I also want the width of the main content to change depending on whether the side bar is open or closed. I want the side bar to make up 20% of the page and the main content to make up 80%. When the side bar is closed I want the main content to change to 100% width.
When I toggle the side bar menu the first time the menu closes and the main content changes to 100% width, but when I reopen it doesn't change back to 80%. I'm a bit stuck can't figure why - looks fine to me.
<template>
<div class="main-content" v-bind:style="{ 'width': width + '%' }">
<div id="burger" :class="{ 'active' : isBurgerActive }" #click.prevent="toggle(); changeWidth();">
<slot>
<button type="button" class="burger-button">
<span class="burger-bar burger-bar-1"></span>
<span class="burger-bar burger-bar-2"></span>
<span class="burger-bar burger-bar-3"></span>
</button>
</slot>
</div>
</div>
</template>
<script>
import {store} from '../store.js';
import {mutations} from '../store.js';
export default {
data() {
return {
width: 80
}
},
computed: {
isBurgerActive() {
return store.isNavOpen
}
},
methods: {
toggle() {
mutations.toggleNav()
},
changeWidth() {
if (this.width = 80) {
this.width = 100;
} else if (this.width = 100) {
this.width = 80;
}
console.log(store.isNavOpen);
console.log(this.width);
}
}
}
</script>
It's:
if (this.width == 80)
and not
if (this.width = 80)
The same for this.width == 100
I have an element that I need to dynamically change his size depending on the screen resolution, so far I have a fixed size which is .scroll-sidebars { max-height: 650px; } but this only works in big screens, as you see in the img below, there is a little margin on the bottom, that's what I want
if I use that same height on smaller screens, this is what I get
so I want to avoid that, it doesn't matter that once the height of that panel changes appears a scroll bar on the panel, that is good, all I need is to keep that margin always there at the bottom.
I have a directive, this is an sticky sidebar, I do not know if its helps because maybe we can calculate that height from the directive
.directive('sticky', function($document, $timeout) {
return {
restrict: 'A',
link: function(scope, element) {
var width = $document.width();
if (width > 991) {
$timeout(function() {
angular.element($document).ready(function() {
var stickySidebar = element,
stickyHeight,
sidebarTop,
scrollTop,
stickyStop,
sidebarBottom,
stopPosition;
if (stickySidebar.length > 0) {
stickyHeight = stickySidebar.height();
sidebarTop = stickySidebar.offset().top;
}
$document.scroll(function() {
if (stickySidebar.length > 0) {
scrollTop = $document.scrollTop() + 52; //stkicy responds to the navbar
if (sidebarTop < scrollTop) {
stickySidebar.css('top', scrollTop - sidebarTop);
sidebarBottom = stickySidebar.offset().top + stickyHeight;
stickyStop = $('.main-content').offset().top + $('.main-content').height();
if (stickyStop < sidebarBottom) {
stopPosition = $('.main-content').height() - stickyHeight;
stickySidebar.css('top', stopPosition);
}
}
else {
stickySidebar.css('top', '0');
}
}
});
$document.resize(function() {
if (stickySidebar.length > 0) {
stickyHeight = stickySidebar.height();
}
});
});
}, 1000);
}else {
console.log('do not apply function because the size is not the proper one');
}
}
};
});
and the html part
<div class="col-md-3">
<div sticky>
<div class="panel">
<div class="panel-heading">
<h3 class="panel-title">Sports</h3><br>
</div>
<div class="panel-group">
<div class="scroll-sidebars">
<accordion close-others="false">
<accordion-group ng-repeat="sport in sports">
<accordion-heading>
<div>
{{::sport.name}}
</div>
</accordion-heading>
<div>
{{::league.name}}
</div>
</accordion-group>
</accordion>
</div>
</div>
</div>
You can use vh on your css, no need to use JS, however, if you need support for old browsers you'll need JS :(
.scroll-sidebars {
height: 100vh;
}
.scroll-sidebars { max-height: 90%; }
Defining a px number will always cause the section to take up that many pixels. No matter the screen size. so if I have a large screen with a low resolution, the same issue occurs.
When using percentages, the CSS uses the browser's height and width to determine how large to make the containing section.
Instead of $document.resize() use $window.onresize which is pure javascript onresize event. Before doing this add $window dependency
CODE
$window.onresize = function() {
if (stickySidebar.length > 0) {
stickyHeight = stickySidebar.height();
}
};
Hope this could help you, Thanks.
You could use the function calc assuming you do not care about old browsers not supporting it :
.scroll-sidebars {
max-height: calc(100% - 50px);
}
The above rule will set the height to 100% of the container's height, minus 50px (adjust this value to your needs).
You could define a height according to the viewport height. The viewport is the visible portion of the document. Note that you can use it with these browsers. The following makes use of the vh unit, which stands for viewport height.
.scroll-sidebars {
max-height: calc(100vh - 100px);
}
So I have this layout here .
<div class="content">
<div class="direction_left"></div>
<div class="direction_right"></div>
<div class="direction_up"></div>
<div class="direction_down"></div>
</div>
<div id="game">
<div id="pos"></div>
</div>
4 divs, if I hover on top it (should) scroll top, if I hover on bottom it (should) scroll bot, left scrolls left and right scrolls right!
On mouse hover on any of the four divs inside the content will scroll the page accordingly.
Important h_amount is horizontal and v_amount is for vertical
function scroll_h() {
console.log('scrolling left and right'+h_amount);
$('#game').animate({
scrollLeft: h_amount
}, 100, 'linear',function() {
if (h_amount != '') {
scroll_h();
}
});
}
function scroll_v() {
console.log('scrolling up and down'+v_amount);
$('#game').animate({
scrollTop: v_amount
}, 100, 'linear',function() {
if (v_amount != '') {
scroll_v();
}
});
}
and then on hover I call
$('.direction_right').hover(function() {
console.log('scroll right');
h_amount = '+=50';
scroll_h();
}, function() {
h_amount = '';
});
$('.direction_up').hover(function() {
console.log('scroll up');
v_amount = '-=50';
scroll_v();
}, function() {
v_amount = '';
});
FULL fiddle here
The problem, I cannot understand why it does not work for up and down. I think my script is correct so am thinking maybe css which is a general weakness of mine might be wrong :D help!
I think I found my answer, please correct me if I am wrong!
Found this question here which asks why his scrollTop() wont animate, and the answer says that window does not have a scrollTop() property, and to use body or html instead (depends on browser, so use both).
Therefore I think my #game div does not have a scrollTop() property either! So I replaced it with html,body and its now working :D.
*
P.S. Don't really know if its actually working or "working".
*
new fiddle here
Also changed that gradient background because it made me dizzy!
I'm currently working on an iPad JS/HTML app, using native scrolling to view a large bar chart svg.
I'm looking to have the labeling along the x and y axes persist along the top and left of the graph. Basically, the graph should slide around freely underneath the labels, which themselves will only move in the appropriate direction (eg, the x axis header will shift its labels over as you scroll left, and the y axes labelings for vertical scrolling)
I currently have some css to do this, but the native scrolling moves a lot faster than my javascript to sync the panels up. There's sort of this elastic interplay as one element is dragged faster than the other. It all plops into the correct place once the scroll animation stops, but the interaction looks pretty janky when scrolling is going on.
Is there a better way to tackle this problem? Are their other events I could tap into? Is there a way to force multiple scrollable divs to react to the same scroll event without manual js position calculations? Or is the lag unavoidable due to native scrolling being offloaded to the gpu?
/* CSS */
.fixedaxis {
position:absolute;
top: 0px;
left: 0px;
background: blue;
}
#chartheader {
z-index:5;
}
#sidebar {
z-index:6;
}
.scroll {
overflow: auto;
-webkit-overflow-scrolling: touch;
}
/* relevant html */
<div id="content" style="position: relative;">
<div id="chartheader" class="fixedaxis"></div>
<div id="sidebar" class="fixedaxis"></div>
<div class="scroll">
<section id="barchart">
</section>
</div>
</div>
/*javascript */
var scroller = $('.scroll');
var tableHeader = $('#chartheader');
var sidebar = $('#sidebar');
scroller.on('scroll', function() {
console.log("scrolling: " + this.scrollLeft);
tableHeader.css('left', (-1 * this.scrollLeft) + 'px');
sidebar.css('top', (-1 * this.scrollTop) + 'px');
});
So it turns out, you can't. At least, not at this point in time.
According to the Apple Developer docs
https://developer.apple.com/library/ios/#documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
You receive events for scroll when the user's finger is on the screen, and one final scroll event once momentum stops. If the content is gliding around between those two periods, you receive no events.
What we ended up doing is making the sidebar content semi-transparent on the second scroll event, and then making it opaque again after a timeout, with an allowance for repositioning if a single "momentum-end" scroll event fired again.
Something along the lines of:
var lastTimeout;
var numScrolls = 1;
var startTop = 0;
function(event) {
var elem = event.target;
startTop = startTop || elem.scrollTop;
if (lastTimeout) {
clearTimeout(lastTimeout);
} else if (numScrolls == -1) {
/* I've omitted some short circuit logic for other scrolling cases
* but that's why we're going off of -1 here
*/
// stray scroll from native scroll end
$labels[0].scrollTop = elem.scrollTop;
$labels.css('opacity', '1');
startTop = null;
}
// wait for two consecutive scrolls
if (numScrolls > 0) {
$labels.css('opacity', '0.3');
}
++numScrolls;
lastTimeout = setTimeout(function() {
console.log(elem.scrollTop);
$labels[0].scrollTop = elem.scrollTop;
$labels.css('opacity', '1');
lastTimeout = null;
numScrolls = -1;
startTop = null;
}, 1000);
};