Giter VIP home page Giter VIP logo

stickybits's Introduction

StickyBits banner

Make things get sticky …in a good way


npm version unpkg snyk codecov Share on Twitter


StickyBits 🍬

Stickybits is a lightweight alternative to position: sticky polyfills. It works perfectly for things like sticky headers.

Stickybits is awesome because

  • it can add a CSS Sticky Class (.js-is-sticky) when position: sticky elements become active and a CSS Stuck Class (.js-is-stuck) when they become stuck. See useStickyClasses.
  • it loosely mimics position: sticky to consistently stick elements vertically across multiple platforms
  • it does not have the jumpiness that plugins that are built around position: fixed have because it tries to support position: sticky first.
  • in its simplest use case, a scroll event listener will not be used if position: sticky is supported.
  • it is super simple & lightweight
  • it provides a wiki that digs deeply into fundementals of position: sticky and position: fixed and it works with them.

Installation   Setup   Usage   Feature   Options   Examples   Debugging   Notes   Contributing   Wiki


Installing from a package manager

yarn

yarn add stickybits

pnpm

pnpm i stickybits

npm

npm i stickybits

Setup

Add dist/stickybits.min.js

Or as a module with import stickybits from 'stickybits'

Basic Usage

stickybits("selector");

By default, a selected stickybits element will

  • Stick elements to the top of the viewport when scrolled to vertically.
  • Stick elements at the bottom of their parent element when scrolled past.

Key Note: Stickybits expects and works best when the element that will become sticky is wrapped within a parent element that defines when the element starts being sticky and stops being sticky. See below for visual reference.

<main id="some-stickybit-parent">
  <nav id="some-stickybit-nav"></nav>
</main>

useStickyClasses Feature

Stickybits allows customers to add CSS to elements when they become sticky and when they become stuck at the bottom of their parent element.

By default, if position: sticky is supported, StickyBits will exit allowing the browser to manage stickiness and avoid adding a scroll event listener.

If the useStickyClasses argument is set to true then even if a browser supports position: sticky, StickyBits will still add a scroll event listener to add and remove sticky CSS Classes. This option is available so that CSS styles can use when StickyBits elements become sticky or stuck at the bottom of their parent.

To provide more feature richness to the Stickybits experience, a .js-is-sticky--change CSS class is added after the Stickybit element is sticky for a certain duration of scroll. By default this duration of scrolling is the height of the Stickybit element. The scroll duration for when .js-is-sticky--change is added can be modified by providing a number for customStickyChangeNumber option.

To use useStickyClasses:

stickybits("selector", { useStickyClasses: true });

Then, in css you can do:

.some-sticky-element.js-is-sticky {
  background-color: red;
}
.some-sticky-element.js-is-sticky--change {
  height: 50px;
}
.some-sticky-element.js-is-stuck {
  background-color: green;
}

View add css classes for more information on StickyBits CSS Classes.

Options

Vertical Layout Position

By default, a StickyBits element will stick to the top of the viewport when vertically scrolled to.

Stickybits loosely works for bottom positioning as well.

To have a StickyBits element stick to the bottom:

stickybits("selector", { verticalPosition: "bottom" });

Custom Scroll Element

By default, if Stickybits uses window scrolling to define Sticky Elements. An element besides window can be used if window is undefined by selecting the desired scrolling element with the scrollEl option. For more custom sticky featuring, the scrollEl option can be used. However, those implementations require the implementing developers support.

To have Stickybit use an selector besides window:

stickybits("selector", { scrollEl: "an-id" });

StickyBit Sticky Offset

By default, a StickyBits element will have a 0px sticky layout top offset. This means that the element will stick flush to the top of the viewport.

To have a StickyBits element stick with a 20px offset to its vertical layout position:

stickybits("selector", { stickyBitStickyOffset: 20 });

StickyBits Cleanup

To cleanup an instance of Stickybits:

const stickybitsInstancetoBeCleanedup = stickybits("selector");
stickybitsInstancetoBeCleanedup.cleanup();

StickyBits Update

To update the calculations of an instance of Stickybits:

const stickybitsInstancetoBeUpdated = stickybits("selector");
stickybitsInstancetoBeUpdated.update();

Re-calculates each Stickybits instance's offsets (stickyStart, stickyStop). If the Stickybits implementer would like re-calculate offsets when the DOM window is resized or when the url changes. .update() can be invoked within an event listener.

const stickybitsInstancetoBeUpdated = stickybits("selector");
stickybitsInstancetoBeUpdated.update({ stickyBitStickyOffset: 20 });

More Stickybits Update Examples

// when the window is resized
const stickybitsInstancetoBeUpdated = stickybits("selector");
window.addEventListener("resize", () => {
  stickybitsInstancetoBeUpdated.update();
});
// when the url hash changes
window.addEventListener("hashchange", () => {
  stickybitsInstancetoBeUpdated.update();
});

Note: .update does not re-initialize classnames or pre-set calculations. Perhaps the update value can help you with that (see the paragraph below).

StickBits Update Props

Props can be updated to each instance by passing then into the .update function as an object.

// .update({ someProp: somePropValue })
const stickybitsInstancetoBeUpdated = stickybits("selector");
stickybitsInstancetoBeUpdated.update({ stickyBitStickyOffset: 20 });

StickyBits NoStyles

To use StickyBits without inline styles except for position: sticky or position: fixed:

stickybits("selector", { noStyles: true });

StickyBits Custom CSS Classes

To use custom CSS classes for Stickybits, add the appropriate properties and values.

parentClass:

stickybits("selector", { parentClass: "new-parent-classname" });

stickyClass:

stickybits("selector", { stickyClass: "new-sticky-classname" });

stuckClass:

stickybits("selector", { stuckClass: "new-stuck-classname" });

StickyBits useFixed

To not use position: sticky ever, add the following key value to a stickybit initalization.

parentClass:

stickybits("selector", { useFixed: true });

To change all of the CSS classes

stickybits("selector", {
  parentClass: "new-parent-classname",
  stickyClass: "new-sticky-classname",
  stuckClass: "new-stuck-classname",
  stickyChangeClass: "new-sticky-change-classname"
});

StickyBits useGetBoundingClientRect

To not use offsetTop provide the optional boolean useGetBoundingClientRect. This feature is optimal when dealing with things like CSS calc which can throw off offsetTop calculations. Read more about this functionality here.

stickybits("selector", { useGetBoundingClientRect: true });

* For jQuery and Zepto support, read the jQuery notes below.

StickyBits applyStyle

If you want to take control of how styles and classes are applied to elements provide a function applyStyle. This is useful for example if you want to integrate with a framework or view library and want to delegate DOM manipulations to it.

stickybits("selector", {
  applyStyle: ({ classes, styles }, instance) => {
    // Apply styles and classes to your element
  }
});

Examples


Extended Examples


Have another example or question? Feel free to comment. 🙌

Notes

CSS Class Usage

3 CSS classes will be added and removed by Stickybits if position: sticky is not supported or if the useStickyClasses: true option is added to the plugin call. These Classes can be modified as desired. See the With Custom Classes example above.

  • js-is-sticky if the selected element is sticky.
  • js-is-stuck if the selected element is stopped at the bottom of its parent.
  • js-stickybit-parent so that styles can easily be added to the parent of a Stickybits element

Not a Polyfill

Stickybits is not a Shim or Polyfill for position: sticky because full support would require more code. This plugin makes elements vertically sticky very similarly to position: fixed but in a sticky sort of way. Read more about position sticky here or follow its browser implementation here.

Stickybits is a no dependency JavaScript plugin. It provides the smallest API possible in both features and kb size to deliver working sticky elements. This means that opinionated featuring is left out as much as possible and that it works with minimal effort in Frameworks.

CSS when position: sticky is not supported

Sticky Start and Sticky Stop: Because Stickybits is minimal, when position: sticky is not supported Stickybits will use position: fixed which is relative to the browser window. If the StickyBits parent element has a height recognized by the browser, Stickybits will take care of the sticky top and sticky bottom invocation. If the parent's height is not recognized by the browser there will be issues.

Left and Right Positioning: With position: fixed the Stickybit element will work relative to the browser window by default. To work with this issue, there are several options. Some are noted here. More solutions to come!

jQuery and Zepto Usage

Basic

$("selector").stickybits();

With scrollEl

$("selector").stickybits({ scrollEl: "#scrollEl" });

// or

const el = document.querySelector("#scrollEl");
$("selector").stickybits({ scrollEl: el });

With .update

const instance = $("selector").stickybits();
instance.update();

With useStickyClasses

$("selector").stickybits({ useStickyClasses: true });

With verticalPosition

$("selector").stickybits({ verticalPosition: "bottom" });

With stickyBitStickyOffset

$("selector").stickybits({ stickyBitStickyOffset: 20 });

Debugging

Stickybits 2.0 provides the same API but with more debugging feedback.

To view the Stickybits API in it's simpliest form:

const stickybit = stickybits("a selection");
console.log(stickybit);

For more debugging and managing Stickybits, view the wiki.


Utility properties

Stickybits provides both version and userAgent properties which were added to offer insight into the browser and Stickybits.

These utility properties can be accessed as direct child properties of the instantiated Stickybits item.

const stickybit = stickybits("a selection");
stickybit.version; // will show the version of stickybits being used
stickybit.userAgent; // will show which userAgent stickybits is detecting

Browser Compatibility

Stickybits works in all modern browsers including Internet Explorer 9 and above. Please file and issue with browser compatibility quirks.

Contributing

Please contribute to Stickybits by filing an issue, responding to issues, adding to the wiki, or reaching out socially—etc.

Stickybits is a utility. It may often not be needed! With shared understanding of position: sticky and position: fixed along with product awareness, Stickybits can improve as can a shared understanding of the "sticky element issue". Is this paragraph over-reaching? Yes! Help improve it.

Thanks

This plugin was heavily influenced by Filament Group's awesome Fixed-sticky jQuery plugin. Thanks to them for getting my mind going on this a while back. Thanks to Peloton Cycle's Frame Throttle for an insightful solve for optimizing frame throttling.

Architecture discussions and Pull Request help has been provided by Jacob Kelley, Brian Gonzalez, and Matt Young. It is much appreciated!


Created and maintained by Jeff Wainwright with Dollar Shave Club Engineering.

More great contributors

stickybits's People

Contributors

alexey-m-ukolov avatar anilanar avatar artjomsimon avatar artskydj avatar athena0304 avatar baklazaner avatar bkeroackdsc avatar briangonzalez avatar danielruf avatar dependabot-preview[bot] avatar dependabot[bot] avatar eddiesigner avatar frankmerema avatar greenkeeper[bot] avatar hacknug avatar jnachtigall avatar kodiakhq[bot] avatar krasnoukhov avatar krinkle avatar maurer2 avatar mbark avatar nextgenthemes avatar panoply avatar pestbarn avatar pull[bot] avatar renovate[bot] avatar tvald avatar ykdr8 avatar yowainwright avatar yuseiueno avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

stickybits's Issues

Consider using a passive scroll listener, if possible

Is your feature request related to a problem? Please describe.

Lighthouse currently flags sites using Stickybits as 'Does not use passive listeners to improve scrolling performance'

Describe the solution you'd like

Would a passive scroll listener be appropriate for Stickybits? Could the listener be made passive? It looks like this happened at one point in Stickybits (https://github.com/dollarshaveclub/stickybits/pull/46/files) but it seems to have been lost in code refactoring?

Additional context

Screenshot 2020-03-30 at 10 27 01

https://web.dev/uses-passive-event-listeners/

is-sticky is not being added despite being sticky, if scrolling down fast

Describe the bug
The 'is-sticky' class is not being added, despite the "position:sticky" is being added by stickybits. I can trigger the error if I scroll down a page very fast - it some times takes a few tries before it happens though).

See this video.

To Reproduce
Steps to reproduce the behavior:

  1. Go to https://thejewelleryroom.com/jewellery
  2. Scroll down very fast (try CMD+ARROW DOWN on mac, or perhaps CTRL+ARROW DOWN on windows, to go to bottom of page)
  3. The stuck bar will be stuck, but is missing "is-sticky" class.

Expected behavior
Should receive "is-sticky" whenever position:sticky is set on the object.

Screenshots
See video:

ezgif-7-e2f87317ab2b

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Config

stickybits(this.$refs.productListHeader, {
	useStickyClasses: true,
	stickyClass: 'is-sticky',
	stuckClass: 'is-sticky',
	stickyBitStickyOffset: (this.$isMobile()) ? 61 : 144
});

State change callback

Is your feature request related to a problem? Please describe.
We use your plugin in Vue and need to update the content of the sticky element, when it becomes sticky. As far as I understand the documentation, there is currently no callback method to execute functionality, when the element becomes or stops to be sticky.

Describe the solution you'd like
There should be an option, to define a callback or bind to a custom event, to execute additional code, when the state of the sticky element changes (sticky/non-sticky).

Describe alternatives you've considered
Did not find a good one.

Additional context
In our case, we have a sidebar, that becomes sticky, once the header would cover it. In this case, some of the elements need to collapse by default in the sidebar, so it's size is reduced.

Top value removed duing js-is-stuck class is added when useStickyClasses: true is used

Describe the bug
I noticed that the top value of the sticky element is removed when you enable useStickyClasses. This unintuitive because if you just use the default stickybits, the top value (even if it is 0px) remains. What happens when you cross the threshold is that the sticky element jumps to the starting position of the element. This creates some visual issues that would normally not be there with just sticky.

To Reproduce
Example code pen here: https://codepen.io/djphan91/pen/MWjawJL

Expected behavior
I would expect that if you enable stickyClasses that the top value is not removed and the position sticky + top css is consistent with the default use of the stickybits init call.

Ideally I'd like to use the classes to track the state of the sticky element but if the class changes come with css inline style changes then that is undocumented or unintended behaviour.

Desktop (please complete the following information):

  • OS: MacOS
  • Browser Chrome
  • Version Version 86.0.4240.198 (Official Build) (x86_64)

Additional context
You can take my code pen and remove the useStickyClasses values and see the difference when comparing the UX behaviour of my sticky element with and without the behaviour.

Calling updates, allows not to set a new stickyBitStickyOffset value

Describe the bug
When calling this.stickybitInstance.update({ stickyBitStickyOffset: <my-new-value> }), the top spacing of the sticky element is not adjusted. It always seems to use the initial value, even though, the prop is updated on the instance.

To Reproduce

const stickybitsInstancetoBeUpdated = stickybits("selector");
stickybitsInstancetoBeUpdated.update({ stickyBitStickyOffset: 20 });

Expected behavior
The sticky element uses the newly defined stickyBitStickyOffset as top value.

Screenshots

Desktop (please complete the following information):

  • OS: macOS 10.15
  • Browser: Chrome
  • Version: 83

Additional context
Stickybits: 3.7.7

I think the following ticket on the old repository documents the same issue, even if I was not able to call the recalculation from outside myself: dollarshaveclub#663

Inline styles are not removed when noStyles option is enabled

Describe the bug
With noStyles option enabled, position in inline styles of the sticky element does not get cleared when returning to the initial position.

To Reproduce
Steps to reproduce the behavior:

  1. Go to https://codepen.io/angkhaustova/pen/xxEXzzN
  2. Resize the preview until it can be scrolled
  3. Scroll down
  4. Scroll back up
  5. The sticky element still has position: fixed

Expected behavior
position: fixed should be removed from styles attribute.

Screenshots
image

Desktop (please complete the following information):

  • OS: iOS
  • Browser: chrome
  • Version: 87.0.4280.88

Stickybits isn't adding padding to parent element when position is changed to fixed

Describe the bug
when sticky bits is initiating it gets all the elements with the selector passed and calculated and sticky start and end portion for each one.
so when having multiple sticky elements in the page and the first element is converted to a fixed position the document flow will change(the element is now out of document flow and the parent will shrink) this will affect the proceeding element sticky start position

To Reproduce
Having any element use the sticky plugin and then scroll to make the element position fixed notice the parent element will shrink
Expected behavior
to emulate sticky positioning the element shouldn't be removed from the document flow I suggest adding padding to the parent element or adding an empty div with the same height
Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
the main problem is that the sticky start position for the element will be broken and the user will have to scroll more distance for it to work

Empty style attribute being left on head element after initialisation

Describe the bug
Empty style attribute being left on head element after initialisation.

To Reproduce
Steps to reproduce the behaviour:
Load page with stickybits initialised

Expected behaviour
Shouldn't be any unrequired attributes

Desktop (please complete the following information):

  • OS: Mac OS Monterey
  • Browser: Chrome
  • Version: 105.0.5195.125

useStickyClasses option adds classes early

Bug
When using sticky classes, they are triggered on .js-stickybit-parent rather than the top of the activated element itself. If there are elements within .js-stickybit-parent and above the activated element , the classes are added before the element becomes sticky. see the video link attached.

Steps to reproduce the behavior:

  1. set useStickyClasses: true
  2. add html to the element parent.
  3. Scroll down and notice the .js-is-sticky class is added when .js-stickybit-parent hits the top of the viewport rather than when the element becomes sticky.

Expected behavior
classes are added when the element becomes sticky

Bug Video
https://www.loom.com/share/9c6870eceaf147f29e5298945dae3f14

Add classes to any other DOM element

I would like to be able to react all over a page to changes of the sticky status. Therefore it would be wonderful if I could give each stickybit instance a name and tell to which other DOM element classes should be added, depending on the sticky status.

stickybits("selector", {
   useGetBoundingClientRect: true,
   name: 'customname',
   addClassesTo: 'anotherselector'
});

The anotherselector would then receive the classes js-is-sticky-customname,
js-is-stuck-customname and js-stickybit-parent-customname.

I could then style any other element: .anotherselector.js-is-stuck-customname .anyelement

This feature request is the same as in the old repository: dollarshaveclub#101

Destroy feature?

Is your feature request related to a problem? Please describe.
I am developing for a SPA and weird things happen on page change -- it seems to work on current page, but when switching to other pages that use same components that have sticky, then it seems to create some issues.

Describe the solution you'd like
A way to destroy stickybits on beforeDestroy callbacks.

Basic usage does not work in chrome 49

Describe the bug

Basic usage does not work in chrome 49

To Reproduce
Steps to reproduce the behavior:

I copied and pasted the code from the basic usage demo and tested it in Chrome 49, the sticky does not work.

Expected behavior

Do you know if this library supports Chrome 49?

Screenshots

lQLPJyInuv5mdJPNAbTNBLuwqpXI_jcqHI0FnqzsTXx0AA_1211_436

The child element is not sticky.

Desktop (please complete the following information):

  • OS: window 11
  • Browser: Chrome
  • Version: 49

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.