JavaScript: Infinite scroll near bottom in div (70% near bottom) - javascript

I want to implement an infinite scroll inside a div whereby data is fetched when a user scrolls past 70% of the content.
Here is my current code:
element.onscroll = () => {
let dom = document.getElementById('scrollable');
let scrollY = dom.scrollHeight - dom.scrollTop;
let height = dom.offsetHeight;
let offset = height - scrollY;
if (offset == 0 || offset == 1) {
getMoreData()
}
};
Is there a way I can check whether the scroll is over 70%? Without executing getMoreData() multiple time?
I need to make sure that getMoreData function runs only once when scrolling down.
Implementations in Vue are also welcome.

Related

OnScroll Fold the View - React Native

I'm really bad at math, I am trying to calculate the top section of the scrollView as I want it to come on top of the top View to make more place for the scrollView.
I know that I could use Animate.View to accomplish this, But due to a component(RecyclerListView) Im unable to do that. So my Idee is while I scroll Down I move the component ItemList up until is at -150 and when I scroll up, I will move until the value scrollY hit 0.
There is already a post here on Stack(here) That displays what I want.
Here is my code.
<View> this is the View i want to cover/fold <View>
<ItemList style={{top:scrollY}}
onScroll={(nativeEvent, offsetX, offsetY)=>{
// when scroll down, scrollY should not exeede 0
// when i scroll up, scrollY should not be more -150
var maxScroll = 0;
var minScroll = -150;
// How should i calculate and set setScrollY()
}}
columnPerRaw={columnPerRar}
itemHeight={150}
onIni={(s: any) => {
setScroll(s);
}}
onItemPress={(item, index) => itemClick(item as item)}
items={data ?? []}
renderItem={renderItem}
onEndReached={() => {
if (globalContext.value.panination && !isLoading)
setEffectTrigger(Math.random());
}}
keyName="name"
/>
Update
I have done like #Nnay said but its not to smoth
var maxScroll = 0; // when i scroll up, scrollY should not be more then 0
var minScroll = -150; // when scroll down, scrollY should not exeede -150
var top = lastPos - offsetY;
if (top >minScroll && top <=maxScroll)
setScrollY(top);
lastPos = offsetY;
I have done it like
no matter how you choose to trigger this behaviour (be it scroll position or scroll direction), you should probably use conditional classes.
to get the scroll direction, you will need to store the previous scroll position.
if oldPosition - newPosition < 0 you have scrolled down, else you have scrolled up.
if you want the position of the element to trigger it, you should opt for setting a boolean to true or false which triggers the class. something like:
<View class={{this.show ? 'show' : ''}}>
where show is a boolean local to your component.
your show class can then handle the height of the element which will automatically push the following content down (provided it isn't positioned absolute) and you can smoothen this by using transition: .25s in css. otherwise it will jump
your scroll handler could then set the boolean depending on the scroll direction with something like:
this.show = oldPos - newPos > 0;
which will return true if the user has scrolled up which seems to be the behaviour you referenced in the link
let me know if this satisfies your question so I can edit and adapt the answer accordingly
ps: if you just set the top of your element while it has a static position, it won't work. also it will just jump to that position since you're not smoothing anything
EDIT: if CSS animations and transitions don't work and you cannot use react-native's Animate.View, the only option that doesn't require external packages is to set an interval animation if react-native doesn't support the AnimationsAPI
I haven't tried using the AnimationsAPI in react-native yet so I would advise to test that first since it's much more performant.
if you want to use the interval animation, I would still recommend sticking to the boolean solution above, but removing the class and adding a height variable in the style attribute of the <View>
make sure to store your interval in a variable so you can clear it.
if (this.height < 150 /* no idea what height you need, play around */ && show && !this.interval){
this.interval = setInterval(() => {
this.height += 1;
}, 10)
} else if (this.height > 0 && !show && !this.interval) {
this.interval = setInterval(() => {
this.height -= 1;
}, 10)
} else {
clearInterval(this.interval)
}
something like this should work. keep in mind that I haven't used interval animations in years so you might need to play around with some values

Get scroll value on element with position:fixed

I have a page with a header section. In it, two blocks that move sideways after scrolling or dragging on the mobile.
I am trying to set the scrolling for the header, but I want too that the rest of the page stays in place until the side blocks reach left: -50% and right:-50%.
I have an event scroll set to header, with pageYoffset values.
I tried to set the rest of the content the page gives to the section with the position:fixed, but then the scroll does not work anymore, and do not count pageYoffset.
Do you have any ideas how to get around it, so that the rest of the page would scroll only after the full unveiling of the header?
(in short, the pink section should be on top and wait until the header disappears)
let current = $(window).scrollTop();
let windowHeight = $(window).height();
let eleLeft = $(".cd-half-left");
let eleRight = $(".cd-half-right");
let currPositionLeft = eleLeft.position().left;
let currPositionRight = eleRight.position().right;
let headerHeaight = $(".cd-section").height();
let halfBlockWidth = $(".cd-half-block").width();
let windowWidth = $(window).width();
$(window).scroll(function (event) {
current = $(window).scrollTop();
console.log({total:total,current:current});
var newPosition = ((current / headerHeaight)*100) / 2;
console.log(newPosition);
eleLeft.css({left:"-"+newPosition+'%'});
eleRight.css({right:"-"+newPosition+'%'});
});
FIDDLE
A solution would be not to use window scroll but instead handle scroll gesture (from mousewheel and touchmove) to control left and right panel, and prevent actual scroll when the panels are not fully opened.
so instead of $(window].scroll(handler), try with $('.cd-block').bind('mousewheel', handler) and $('.cd-block').bind('mousewheel', handler)
The handler being:
function updateCurrent(event) {
if (current >= 50) {
current = 50;
} else {
if (current <= 0) {
current = 0;
}
// if below 50 we cancel the event to prevent the scroll
event.originalEvent.preventDefault();
}
eleLeft.css({left:"-"+current+'%'});
eleRight.css({right:"-"+current+'%'});
}
Here is a buggy but working solution (keyboard space, up and down should be handled too):
fiddle

Scroll above element height + content change

I have looked around to see if this has been covered, but no dice.
I'm building a website using HTML5, CSS3 (+ animations), bootstrap, Vanilla JS and jQuery. The behavior I'm looking to induce for the site is one where a user visits a landing page and the scroll bar is situated at the top. Then, upon scrolling down past a certain point, a completely different screen takes over.
Here's the (maybe) tricky part:
I want for a visitor to be able to scroll all the way up on this 2nd screen as high as the scrollbar can go. Only once the scroll bar is at the top and they try try to scroll up past the top of the current window, that the original, first screen comes back in to play (while still persisting the progress they made on the 2nd screen if they decide to scroll back down).
Negative scrollTop/browser/window heights to trigger an event in which a user can navigate between pages using a maxed out up-top scrollbar? (& should I use a framework?) Much appreciated!
You could duplicate elements while scrolling,
I made a plunker to give you an idea
jQDocument.on("scroll", () => {
if(jQDocument.scrollTop() < 200) {
//Duplicate elements on top while scrolling up
let topScreen = detectTopScreen()
let indexTopScreen = getIndex(topScreen)
let screenIndexToDuplicate
if(indexTopScreen > 0) {
screenIndexToDuplicate = indexTopScreen - 1
} else {
screenIndexToDuplicate = maxIndex
}
let screenToPrepend = originalLoopDiv.children().eq(screenIndexToDuplicate).clone()
loopDiv.prepend(screenToPrepend)
if(loopDiv.children().length > 6) {
loopDiv.children().eq(loopDiv.children().length - 1).remove()
}
}
if(jQDocument.scrollTop() + jQWindow.outerHeight() > jQBody.outerHeight() - 200) {
//Duplicate elements on bottom while scrolling down
let bottomScreen = detectBottomScreen()
let indexBottomScreen = getIndex(bottomScreen)
let screenIndexToDuplicate
if(indexBottomScreen < maxIndex) {
screenIndexToDuplicate = indexBottomScreen + 1
} else {
screenIndexToDuplicate = 0
}
let screenToAppend = originalLoopDiv.children().eq(screenIndexToDuplicate).clone()
loopDiv.append(screenToAppend)
if(loopDiv.children().length > 6) {
loopDiv.children().eq(0).remove()
}
}
})

Infinite Scrolling of <table /> element in react.js

I have a particularly interesting problem with respect to infinite scrolling using react.js.
The goal here is to make sure no matter how large a table becomes, we only ever let render() return a fixed subset of all rows.
We will let lowerVisualBound and upperVisualBound denote the subset of rows to render() onto the DOM.
Note these bounds are set to be larger than the viewport, causing a scrollbar to appear.
We will modify lowerVisualBound and upperVisualBound as the user scrolls.
Here, we further denote
height as the height of the visible portion of the table
totalHeight as the height of the entire table (that includes all rows between lowerVisualBound and upperVisualBound
scrollTop and state.lastScrollTop as the current and previous scroll top respectively
The Following Snippet Kind of Does the Trick - Except the scrollbar itself does not change position after additional data has been loaded (i.e. upper or lower VisualBound reset). This causes the user's view of the data to jump.
const rowDisplayBoundry = 2 * this.props.pageSize;
if (scrollTop < this.state.lastScrollTop && scrollTop <= 0.0 * totalHeight) {
// up scroll limit triggered
newState.lowerVisualBound = Math.max(this.state.lowerVisualBound - this.props.pageSize, 0);
newState.upperVisualBound = newState.lowerVisualBound + rowDisplayBoundry;
} else if (scrollTop > this.state.lastScrollTop && (scrollTop + height) > totalHeight) {
// down scroll limit triggered
newState.upperVisualBound = this.state.upperVisualBound + this.props.pageSize;
newState.lowerVisualBound = newState.upperVisualBound - rowDisplayBoundry;
}
// TODO now what do we set scrollTop to, post these mutations? (presumably using a setTimeout())
Can anyone suggest an algorithm to compute a new scrollTop such that changing the visual bound preserve the user's view?
Note I believe this should be theoretically possible because the # of rows between upper & lower visual bound is set to be > what can be displayed in the viewport. Therefore, after each mutation in those bounds, the user does not lose any rows that he was viewing immediately before the mutation. It is only a matter of computing the correct location the scrollbar post-mutation.
The following appears to have worked ... though not sure if there are corner cases where it doesn't (please excuse the rather liberal use of jQuery selector for this demostration)
handleScroll: function (e) {
const $target = $(e.target);
const scrollTop = $target.scrollTop();
const height = $target.height();
const totalHeight = $target.find("tbody").height();
const avgRowHeight = totalHeight / (this.state.upperVisualBound - this.state.lowerVisualBound);
/**
* always update lastScrollTop on scroll event - it helps us determine
* whether the next scroll event is up or down
*/
var newState = {lastScrollTop: scrollTop};
/**
* we determine the correct display boundaries by keeping the distance between lower and upper visual bound
* to some constant multiple of pageSize
*/
const rowDisplayBoundry = 2 * this.props.pageSize;
if (scrollTop < this.state.lastScrollTop && scrollTop <= 0) {
// up scroll limit triggered
newState.lowerVisualBound = Math.max(this.state.lowerVisualBound - this.props.pageSize, 0);
newState.upperVisualBound = newState.lowerVisualBound + rowDisplayBoundry;
// if top most rows reached, do nothing, otherwise reset scrollTop to preserve current view
if (!(newState.lowerVisualBound === 0))
setTimeout(function () {
$target.scrollTop(Math.max(scrollTop + this.props.pageSize * avgRowHeight, 0));
}.bind(this));
} else if (scrollTop > this.state.lastScrollTop && (scrollTop + height) >= totalHeight) {
// down scroll limit triggered
newState.upperVisualBound = this.state.upperVisualBound + this.props.pageSize;
newState.lowerVisualBound = newState.upperVisualBound - rowDisplayBoundry;
setTimeout(function () {
// TODO ensure that new scrollTop doesn't trigger another load event
// TODO ensure this computationally NOT through flagging variables
$target.scrollTop(scrollTop - this.props.pageSize * avgRowHeight);
}.bind(this));
}
this.setState(newState);
}

Stop scrolling page, scroll div

I'm creating this landing page: http://tag4share.com/landing/#
Where is located the two galaxy s3 (one white with "Organizador" label on it and a black with "Participante" label), I want to stop scrolling the page and automatically start scrolling the content inside the mobile (an iFrame, div, anything).
Is it possible?
Basically I want to "focus" the scrolling inside a div (and make it work even if the cursor isn't hovering it). Or animate while scrolling without scrolling the body.
Example: http://www.google.com/nexus/5/
On the "Everything you need to capture the moments that matter." part.
My attempt:
var lastScroll;
var currentScroll = $(window).scrollTop();
$(window).scroll(function() {
lastScroll = currentScroll;
currentScroll = $(window).scrollTop();
if($(window).scrollTop() >= 2024 && $(window).scrollTop() < 2500)
{
var difference = currentScroll - lastScroll;
$(".main").css({"margin-top":"-="+currentScroll});
}
});
I've tried to move the main div along with scrolling. It works but it looks really strange (keeps shaking).
Thanks!
I've just tidied up your code a tad, fixed indentation etc.
As for actually scrolling your div when you hit the position, use animate to actually mimic the scrolling effect, once you know you have reached the bottom, you can put another if statement within the scroll function to stop resetting the scroll position.
var lastScroll;
var scrollPosition = $(window).scrollTop();
var reachedBottom = false;
var phonePositionTop = $('#phoneContainerID').position().top;
var phonePositionBottom = phonePositionTop + $('#phoneContainerID').height();
$(window).scroll(function() {
if(scrollPosition >= phonePositionTop && scrollPosition < phonePositionBottom && reachedBottom == false){
var difference = currentScroll - lastScroll;
// Keep resetting scroll to the phoneContainerTop position
$(".main").css({"margin-top": phonePositionTop});
var scrollLimit = -100;
if ($('#phoneContainerID').position().top < scrollLimit) {
//Once the scroll limit is less than -100 (scrolled up 100 pixels)
// Disable our 'pause' effect, and continue
reachedBottom = true;
}
}
});
I haven't tested this, however I was just giving you an idea of where to go from here.
I hope I have helped a little!

Categories

Resources