-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow containers other than window to scroll #41
base: master
Are you sure you want to change the base?
Changes from all commits
cd26988
3fdef1c
642ed83
2b6977e
489f279
e80dcf2
d137cef
76099f4
9b3bb90
2d1c75b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import React, { Component } from 'react' | ||
import ScrollableAnchor, { configureAnchors } from '../../../src' | ||
import Section from './Section' | ||
|
||
const sections = [ | ||
{id: 'section1', label: 'Section 1', backgroundColor: 'red'}, | ||
{id: 'section2', label: 'Section 2', backgroundColor: 'darkgray'}, | ||
{id: 'section3', label: 'Section 3', backgroundColor: 'green'}, | ||
{id: 'section4', label: 'Section 4', backgroundColor: 'brown'}, | ||
{id: 'section5', label: 'Section 5', backgroundColor: 'lightpink'} | ||
] | ||
|
||
const styles = { | ||
offsetUp: { | ||
marginTop: '-549px' | ||
}, | ||
extraTall: { | ||
height: '700px' | ||
}, | ||
scrollingDiv: { | ||
height: '50vh', | ||
overflowY: 'scroll', | ||
marginTop: '25vh', | ||
width: '50%', | ||
marginLeft: '25%', | ||
position: 'relative' | ||
} | ||
|
||
} | ||
|
||
export default class Example5 extends Component { | ||
|
||
componentWillMount() { | ||
configureAnchors({containerId: 'scrolling-div'}) | ||
} | ||
|
||
renderSection = (section) => { | ||
const props = {...section, sections, style: styles.extraTall} | ||
return ( | ||
<div key={section.id}> | ||
<ScrollableAnchor id={section.id}> | ||
<Section {...props}/> | ||
</ScrollableAnchor> | ||
</div> | ||
) | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
{ this.props.renderHeader(true, sections, true) } | ||
<div id='scrolling-div' style={styles.scrollingDiv}> | ||
{ sections.map(this.renderSection) } | ||
</div> | ||
</div> | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,15 @@ | ||
import jump from 'jump.js' | ||
import zenscroll from 'zenscroll' | ||
import { debounce } from './utils/func' | ||
import { getBestAnchorGivenScrollLocation, getScrollTop } from './utils/scroll' | ||
import { getHash, updateHash, removeHash } from './utils/hash' | ||
|
||
const defaultConfig = { | ||
offset: 0, | ||
scrollDuration: 400, | ||
keepLastAnchorHash: false, | ||
keepLastAnchorHash: false | ||
} | ||
|
||
|
||
class Manager { | ||
constructor() { | ||
this.anchors = {} | ||
|
@@ -19,30 +20,44 @@ class Manager { | |
this.forceHashUpdate = debounce(this.handleHashChange, 1) | ||
} | ||
|
||
setContainer = () => { | ||
// if we have a containerId, find the scrolling container, else set it to window | ||
if (this.config.containerId) { | ||
this.config.container = document.getElementById(this.config.containerId) | ||
this.config.scroller = zenscroll.createScroller(this.config.container, this.config.scrollDuration, this.config.offset) | ||
} else { | ||
this.config.container = window | ||
this.config.scroller = zenscroll | ||
} | ||
} | ||
|
||
addListeners = () => { | ||
window.addEventListener('scroll', this.scrollHandler, false) | ||
this.config.container.addEventListener('scroll', this.scrollHandler, false) | ||
window.addEventListener('hashchange', this.handleHashChange) | ||
} | ||
|
||
removeListeners = () => { | ||
window.removeEventListener('scroll', this.scrollHandler, false) | ||
this.config.container.removeEventListener('scroll', this.scrollHandler, false) | ||
window.removeEventListener('hashchange', this.handleHashChange) | ||
} | ||
|
||
configure = (config) => { | ||
this.config = { | ||
...defaultConfig, | ||
...config, | ||
...config | ||
} | ||
} | ||
|
||
goToTop = () => { | ||
if (getScrollTop() === 0) return | ||
this.forcedHash = true | ||
window.scroll(0,0) | ||
if (getScrollTop(this.config.container) === 0) return | ||
this.config.scroller.toY(0, this.config.scrollDuration) | ||
} | ||
|
||
addAnchor = (id, component) => { | ||
// if container is not set, set container | ||
if (!this.config.container) { | ||
this.setContainer() | ||
} | ||
// if this is the first anchor, set up listeners | ||
if (Object.keys(this.anchors).length === 0) { | ||
this.addListeners() | ||
|
@@ -61,7 +76,7 @@ class Manager { | |
|
||
handleScroll = () => { | ||
const {offset, keepLastAnchorHash} = this.config | ||
const bestAnchorId = getBestAnchorGivenScrollLocation(this.anchors, offset) | ||
const bestAnchorId = getBestAnchorGivenScrollLocation(this.anchors, offset, this.config.container) | ||
|
||
if (bestAnchorId && getHash() !== bestAnchorId) { | ||
this.forcedHash = true | ||
|
@@ -81,20 +96,16 @@ class Manager { | |
|
||
goToSection = (id) => { | ||
let element = this.anchors[id] | ||
let viewHeight = this.config.container.innerHeight || this.config.container.clientHeight | ||
let offset = this.config.offset + viewHeight/2 | ||
if (element) { | ||
jump(element, { | ||
duration: this.config.scrollDuration, | ||
offset: this.config.offset, | ||
}) | ||
this.config.scroller.center(element, this.config.scrollDuration, offset) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused about why use If element's height is larger than container, it will not show the top of the element, but maybe we want to see the element from the beginning of it. Maybe there is another solution? @dbramwell There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did this 10 months ago, so the details are a bit fuzzy. If you look at the demo, it contains elements that are larger than the container and it does work as expected. I think I did it this way because of the way zenscroll works. From the docs: "If you want you can also define an offset. The top of the element will be upwards from the center of the screen by this amount of pixels." So, with an offset of zero, the top of the element would end up in the centre of the container. We want the top of the element to be at the top of the container so we need an offset of half the container's height (plus any offset requested in the config). Does that answer the question? |
||
} else { | ||
// make sure that standard hash anchors don't break. | ||
// simply jump to them. | ||
element = document.getElementById(id) | ||
if (element) { | ||
jump(element, { | ||
duration: 0, | ||
offset: this.config.offset, | ||
}) | ||
this.config.scroller.center(element, 0, this.config.offset) | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about
this.config.containerId
could be id's string or container's ref ?As it is possible that parent component which is using
ScrollableAnchor
may maintain a container's ref~There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's a valid request I guess. I don't think this library is really maintained any more so it's unlikely this will ever get merged
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a version of this library to my library collection that has some added improvements already (Sadly no documentation yet, i'm working on it though)
https://github.com/leon-marzahn/marzahn-dev/tree/main/libs/react/scroll-anchor