Code Monkey home page Code Monkey logo

sweet-scroll's Introduction

sweet-scroll

CircleCI Status npm version License

ECMAScript2015+ & TypeScript Friendly, dependency-free smooth scroll library.

🍭 See Demo

Features

  • Dependecy-free!!
  • ECMAScript2015+ & TypeScript friendly
  • Use requestAnimationFrame API
  • Supports vertical and horizontal scroll
  • Supports dynamic trigger (event delegation)
  • Supports container for the scroll
  • Supports many easing types
  • Supports server-side rendering (Can load without putting out errors.)

Migration Guide

See the Migration Guide

Table of Contents

Usage

1. Install

via NPM

$ npm install sweet-scroll
use
import SweetScroll from 'sweet-scroll';

via MANUAL

  1. Download the sweet-scroll.min.js
  2. Load it in the script tag.
<script src="sweet-scroll.min.js"></script>

via CDN (UNPKG)

<script src="https://unpkg.com/sweet-scroll/sweet-scroll.min.js"></script>

2. Setup of HTML

<a href="#intro" data-scroll>Go to Introduction</a>
...
<div id="intro">Introduction</div>

3. Initialize SweetScroll

You need to initialize an instance after DOMContentLoaded.

document.addEventListener(
  'DOMContentLoaded',
  () => {
    const scroller = new SweetScroll({
      /* some options */
    });
  },
  false,
);

Options

The following options are applied by default. It can be customized as needed.

{
  trigger: '[data-scroll]',       // Selector for trigger (must be a valid css selector)
  header: '[data-scroll-header]', // Selector or Element for fixed header (Selector of must be a valid css selector)
  duration: 1000,                 // Specifies animation duration in integer
  easing: 'easeOutQuint',         // Specifies the pattern of easing
  offset: 0,                      // Specifies the value to offset the scroll position in pixels
  vertical: true,                 // Enable the vertical scroll
  horizontal: false,              // Enable the horizontal scroll
  cancellable: true,              // When fired wheel or touchstart events to stop scrolling
  updateURL: false,               // Update the URL hash on after scroll (true | false | 'push' | 'replace')
  preventDefault: true,           // Cancels the container element click event
  stopPropagation: true,          // Prevents further propagation of the container element click event in the bubbling phase

  // Callbacks
  before: null,
  after: null,
  cancel: null,
  complete: null,
  step: null,
}

Easings

Supports the following easing.

Built-in (22)

  • Normal
    • linear
  • Quad
    • easeInQuad
    • easeOutQuad
    • easeInOutQuad
  • Cubic
    • easeInCubic
    • easeOutCubic
    • easeInOutCubic
  • Quart
    • easeInQuart
    • easeOutQuart
    • easeInOutQuart
  • Quint
    • easeInQuint
    • easeOutQuint (default)
    • easeInOutQuint
  • Sine
    • easeInSine
    • easeOutSine
    • easeInOutSine
  • Expo
    • easeInExpo
    • easeOutExpo
    • easeInOutExpo
  • Circ
    • easeInCirc
    • easeOutCirc
    • easeInOutCirc

Advanced (9)

Easing functions that are not built in can pass functions directly.

const scroller = new SweetScroll({
  easing: advancedEasingFunction,
});

Elastic

easeInElastic
const easeInElastic = (_, t, b, c, d) => {
  let s = 1.70158;
  let p = 0;
  let a = c;
  if (t === 0) return b;
  if ((t /= d) === 1) return b + c;
  if (!p) p = d * 0.3;
  if (a < Math.abs(c)) {
    a = c;
    s = p / 4;
  } else {
    s = (p / (2 * Math.PI)) * asin(c / a);
  }
  return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p)) + b;
};
easeOutElastic
const easeOutElastic = (_, t, b, c, d) => {
  let s = 1.70158;
  let p = 0;
  let a = c;
  if (t === 0) return b;
  if ((t /= d) === 1) return b + c;
  if (!p) p = d * 0.3;
  if (a < Math.abs(c)) {
    a = c;
    s = p / 4;
  } else {
    s = (p / (2 * Math.PI)) * asin(c / a);
  }
  return a * Math.pow(2, -10 * t) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) + c + b;
};
easeInOutElastic
const easeInOutElastic = (_, t, b, c, d) => {
  let s = 1.70158;
  let p = 0;
  let a = c;
  if (t === 0) return b;
  if ((t /= d / 2) === 2) return b + c;
  if (!p) p = d * (0.3 * 1.5);
  if (a < Math.abs(c)) {
    a = c;
    s = p / 4;
  } else {
    s = (p / (2 * Math.PI)) * Math.asin(c / a);
  }
  if (t < 1) {
    return (
      -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p)) + b
    );
  }
  return (
    a * Math.pow(2, -10 * (t -= 1)) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) * 0.5 + c + b
  );
};

Back

easeInBack
const easeInBack = (_, t, b, c, d, s = 1.70158) => c * (t /= d) * t * ((s + 1) * t - s) + b;
easeOutBack
const easeOutBack = (_, t, b, c, d, s = 1.70158) =>
  c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
easeInOutBack
const easeInOutBack = (_, t, b, c, d, s = 1.70158) =>
  (t /= d / 2) < 1
    ? (c / 2) * (t * t * (((s *= 1.525) + 1) * t - s)) + b
    : (c / 2) * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b;

Bounce

easeOutBounce
const easeOutBounce = (_, t, b, c, d) => {
  if ((t /= d) < 1 / 2.75) {
    return c * (7.5625 * t * t) + b;
  } else if (t < 2 / 2.75) {
    return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b;
  } else if (t < 2.5 / 2.75) {
    return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b;
  }
  return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b;
};
easeInBounce
const easeOutBounce = (_, t, b, c, d) => {
  if ((t /= d) < 1 / 2.75) {
    return c * (7.5625 * t * t) + b;
  } else if (t < 2 / 2.75) {
    return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b;
  } else if (t < 2.5 / 2.75) {
    return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b;
  }
  return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b;
};

const easeInBounce = (x, t, b, c, d) => c - easeOutBounce(x, d - t, 0, c, d) + b;
easeInOutBounce
const easeOutBounce = (_, t, b, c, d) => {
  if ((t /= d) < 1 / 2.75) {
    return c * (7.5625 * t * t) + b;
  } else if (t < 2 / 2.75) {
    return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b;
  } else if (t < 2.5 / 2.75) {
    return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b;
  }
  return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b;
};

const easeInBounce = (x, t, b, c, d) => c - easeOutBounce(x, d - t, 0, c, d) + b;

const easeInOutBounce = (x, t, b, c, d) =>
  t < d / 2
    ? easeInBounce(x, t * 2, 0, c, d) * 0.5 + b
    : easeOutBounce(x, t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;

Live demo

Customizing Tips

Specifying container elements

In the following example we have specified in the container for scrolling the #container.

<div id="container">
  <a href="#heading2" data-scroll>Go to Heading2</a>
  ...
  <h2 id="heading2">Heading2</h2>
</div>
// Specified in the CSS Selector
const scroller = new SweetScroll(
  {
    /* some options */
  },
  '#container',
);

// Specified in the Element
const scroller = new SweetScroll(
  {
    /* some options */
  },
  document.getElementById('container'),
);

Specify fixed header

Add the data-scroll-header attribute in order to offset the height of the fixed header.

<header data-scroll-header></header>

Specify the CSS Selector in header option instead of the data-scroll-header attribute.

const scroller = new SweetScroll({
  header: '#header',
});

Override of options for each element

You can override the default options by passing the option in JSON format to the data-scroll-options.

<a href="#target" data-scroll data-scroll-options='{"easing": "easeOutExpo"}'>Go to Target</a>

If you want to use in non anchor element

Will use the data-scroll attribute instead of href.

<button type="button" data-scroll="+=500">Scroll under 500px</button>

Scrolling animation in another page

The following, Introduce one of the mounting method.

document.addEventListener(
  'DOMContentLoaded',
  () => {
    const scroller = new SweetScroll();
    const hash = window.location.hash;
    const needsInitialScroll = document.getElementById(hash.substr(1)) != null;

    if (needsInitialScroll) {
      window.location.hash = '';
    }

    window.addEventListener(
      'load',
      () => {
        if (needsInitialScroll) {
          scroller.to(hash, { updateURL: 'replace' });
        }
      },
      false,
    );
  },
  false,
);

Live demo

You can also achieve the same thing in other ways by using the provided API.

API

new SweetScroll(options?: PartialOptions, container?: string | Element | Window)

Will generate a SweetScroll instance.

Example:

const scroller = new SweetScroll(
  {
    duration: 1200,
    easing: 'easeOutExpo',
  },
  '#container',
);

SweetScroll.create(options?: PartialOptions, container?: string | Element | Window)

Will generate a SweetScroll instance. (factory method)

Example:

const scroller = SweetScroll.create(
  {
    duration: 1200,
    easing: 'easeOutExpo',
  },
  '#container',
);

to(distance: any, options?: PartialOptions)

Scroll animation to the specified distance. distance to can specify the CSS Selector or scroll position.

Example:

const scroller = new SweetScroll();

// CSS Selector of target element
scroller.to('#footer');

// Object
scroller.to({ top: 1000, left: 20 });

// Array (top:0, left:1000)
scroller.to([0, 1000]);

// Number (Priority to vertical scroll position. by default.)
scroller.to(500);

// String (Relative position)
scroller.to('+=500');
scroller.to('-=200');

toTop(distance: any, options?: PartialOptions)

Vertical scroll animation to the specified distance.

Example:

scroller.toTop(0);

toLeft(distance: any, options?: PartialOptions)

Horizontal scroll animation to the specified distance.

Example:

scroller.toLeft(1500);

toElement($el: Element, options?: PartialOptions)

Scroll animation to the specified Element.

Example:

scroller.toElement(document.getElementById('content'));

update(options: PartialOptions)

Will update the SweetScroll instance. Primarily used in the case of option update.

Example:

scroller.update({
  trigger: 'a[href^="#"]',
  duration: 3000,
});

stop(gotoEnd: boolean = true)

gotoEnd: {Boolean}

Will stop the current scroll animation.

Example:

scroller.stop(true);

destroy()

Will destroy the SweetScroll instance. Disable of the method and event handler.

Example:

scroller.destroy();

Callbacks

In before and after, you will pass the coordinates and the triggering element in the argument. In addition, you can stop the scrolling by return a before in false.

Example:

const scroller = new SweetScroll({
  // Stop scrolling case of trigger element that contains the `is-disabled` class.
  before: (offset: Offset, $trigger: Element | null, scroller: SweetScroll): boolean | void => {
    console.log('Before!!', offset, scroller);
    if ($trigger && $trigger.classList.contains('is-disabled')) {
      return false;
    }
  },

  // If the `wheel` or `touchstart` event is called
  cancel: (scroller: SweetScroll): void => {
    console.log('Cancel!!', scroller);
  },

  // Scroll animation is complete
  after: (offset: Offset, $trigger: Element | null, scroller: SweetScroll): void => {
    console.log('After!!', offset, $trigger, scroller);
  },

  // Scroll animation is complete (`after` or `cancel`)
  complete: (isCancel: boolean, scroller: SweetScroll): void => {
    console.log('Complete!!', isCancel, scroller);
  },

  // Each animation frame
  step: (time: number, scroller: SweetScroll): void => {
    console.log('step', time, scroller);
  },
});

Extends Class:

The following is a pattern to override a method in the inheritance destination class.

import SweetScroll, { Offset } from 'sweet-scroll';

class MyScroll extends SweetScroll {
  protected onBefore(offset: Offset, $trigger: Element | null): boolean | void {
    // Stop scrolling case of trigger element that contains the `is-disabled` class.
    console.log('Before!!', offset);
    if ($trigger && $trigger.classList.contains('is-disabled')) {
      return false;
    }
  }

  protected onCancel(): void {
    console.log('Canell!!');
  }

  protected onAfter(offset: Offset, $trigger: Element | null): void {
    console.log('After!!', offset, $trigger);
  }

  protected onComplete(isCancel: boolean): void {
    console.log('Complete!!', isCancel);
  }

  protected onStep(time: number): void {
    console.log('step', time);
  }
}

Browser Support

Works in IE10+, and all modern browsers.

Scrolling with IE9

It is necessary to use polyfill or ponyfill of requestAnimationFrame.

Example ponyfill

Using raf module.

import raf from 'raf';
import SweetScroll from 'sweet-scroll';

SweetScroll.raf = raf;
SweetScroll.caf = raf.cancel;

CHANGELOG

See the CHANGELOG.md

Contibute

  1. Fork it!
  2. Create your feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request 💪

Bugs, feature requests and comments are more than welcome in the issues.

Development

We will develop using the following npm scripts.

yarn start

Launch the local server and let the demo run. Opening http://localhost:3000 in your browser.

yarn build

Compile TypeScript and create type definitions.

yarn test

Run unit testing with Jest.

License

MIT © tsuyoshiwada

sweet-scroll's People

Contributors

nlemoine avatar renovate-bot avatar wadackel 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  avatar  avatar  avatar  avatar  avatar  avatar

sweet-scroll's Issues

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • chore: replace dependency cpx with cpx2 2.0.0
  • chore: replace dependency npm-run-all with npm-run-all2 5.0.0
  • chore: update dependency browser-sync to v3
  • chore: update dependency prettier to v3
  • chore: update dependency rollup to v4
  • chore: update dependency tslint to v6
  • chore: update dependency typescript to v5
  • chore: update node.js to v17
  • 🔐 Create all rate-limited PRs at once 🔐

Edited/Blocked

These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

circleci
.circleci/config.yml
  • circleci/node 10
npm
package.json
  • @types/jest 24.0.9
  • browser-sync 2.26.3
  • cpx 1.5.0
  • dts-bundle 0.7.3
  • gh-pages 2.0.1
  • jest 24.3.1
  • npm-run-all 4.1.5
  • prettier 1.16.4
  • rollup 1.4.1
  • rollup-plugin-typescript2 0.19.3
  • rollup-watch 4.3.1
  • ts-jest 24.0.0
  • tslint 5.13.1
  • typescript 3.3.3333
  • uglify-es 3.3.9

  • Check this box to trigger a request for Renovate to run again on this repository

Uncaught TypeError: Cannot read property 'stop' of undefined

Looks like there might be a regression with v1.1.0.

The line that errors out is: https://github.com/tsuyoshiwada/sweet-scroll/blob/b0fee7600f3d0ae94d898e73eb366d736e8e6bcb/src/sweet-scroll.js#L264

The code that caused it is here:

  const input = document.querySelector(/* snip */);
  const container = document.querySelector(/* snip */);
  if (input && container) {
    const scroller = new SweetScroll({ duration: 200 }, container);
    scroller.toElement(input);
  }

Glitchy animation advances two sections instead of one

Hiya, hope everything's alright!

So I've been playing with your plugin, and it looks like if you get the rhythm exactly right, you end up with a glitchy animation that jumps two sections instead of just advancing by one section.

For example, sometimes you can get it to go:

1 -> 2
2 -> 3
2 -> 4  # this is the glitch
4 -> 5

I'm not sure if you've noticed this, but it's actually pretty hard for me to replicate consistently.

I've put up a demo page where you can click or press the down arrow to advance through sections.
It seems like if you try to go to the next section a tiny bit before the transition is fully finished, you can get it to snap backwards really quickly and then advance two sections at once.

Maybe an event queue or something could mitigate the issue?

Not too invested in this one, to be honest. Just bringing it to your attention if you've not noticed it before.

Isomorphic

Sweet Scroll not working on isomorphic apps.

window and document is not defined.

This, will help:

let tmpWin = null;
let tmpDoc = null;

if (typeof window !== "undefined") {
  tmpWin = window;
}

if (typeof document !== "undefined") {
  tmpDoc = document;
}

export const win = tmpWin;
export const doc = tmpDoc;

window, document, navigator, requestAnimationFrame, onWheel

Scrolling behind modal

Hi,

I'm using sweet-scroll (it's smooth as butter, by the way!) inside a tall modal, however, toElement scrolls both the modal and the window behind it.

// In context of React using hooks
const scroller = new SweetScroll({
  duration: 1000,
  easing: "easeOutExpo"
}, feedRef.current)
scroller.toElement(el.current, {offset: -75})

Any suggestions?

1.5 / getZoomLevel may result in scrollTop===0 and no scrolling when dev tools are open

Hi @tsuyoshiwada!

Thanks for developing and maintaining the lib! I looked for an ES6 ready lib for scrolling for a long time! :)

Though I found a funny issue today. I was trying to figure out why my container is null on one view I'm working on, while being scrollable on another. My investigation led me to the getScrollable function. I see what you do there, trying to make sure the container is scrollable, though I don't fully understand why the scrolling should not work if a window zoom is bigger than 1.5. :)

Anyway - because of the way a zoom level is calculated in getZoomLevel, having the dev tools open aside makes the outerWidth/innerWidth ratio bigger than 1. And the moment it gets bigger than 1.5 - the scrollTop set on the element gets rounded to 0 which results in no scrolling behaviour. :)

I might not be aware of all factors that led you to the decision of disabling scroll for zoom levels bigger than 1.5, but the first thing that came to my head is to compare the zoom level calculated from outerWidth/innerWidth with outerHeight/innerHeight.

For now, a temporary solution I use is to change scrollTop of the container from 0 to 1 before calling the Sweet Scroll.

Let me know if you need more info! Thanks again for working on the lib! ;)

Horizontal Scrollデモが動作しない

私の固有の問題かもしれませんので、以下は参考レベルとしてください。
不要でしたら適宜クローズ願います

現状デフォルトオプションが以下となっており

  // Default options
  static defaults = {
   // ...省略
    verticalScroll: true,           // Enable the vertical scroll
    horizontalScroll: false,        // Enable the horizontal scroll
   // ...省略
  }

horizontal.htmlで以下のように設定しています。

    var sweetScroll = new SweetScroll({
      horizontalScroll: true
    });

そのためconstructorでmergeした際にhorizontalScroll, verticalScrollともにtrueとなっているように見えます。

  constructor(options = {}, container = "body, html") {
    this.options = Util.merge({}, SweetScroll.defaults, options);
    this.container = this._getContainer(container);
    this.header = $(this.options.header);
    this.el = $$(this.options.trigger);
    this.tween = new ScrollTween(this.container);
    this._bindContainerClick();
  }

それによって以下でdirectionyとなり、結果scrollableFindundefinedを返してるように見受けられます。

  _getContainer(selector) {
    const {verticalScroll, horizontalScroll} = this.options;
    let direction;
    if (verticalScroll || (verticalScroll && horizontalScroll)) {
      direction = "y";
    } else if (horizontalScroll) {
      direction = "x";
    }
    return Dom.scrollableFind(selector, direction);
  }

よって以下のようにすると動くんですが、こちらでしか再現してませんし、設計**からもはずれると思うので参考としてください。

horizontal.html

    var sweetScroll = new SweetScroll({
      verticalScroll: false
      horizontalScroll: true
    });

Changelog?

Where can i see what features you added, bugs you fixed between releases?

Page disappears in Chrome v67

Some parts or sometimes the whole page disappears after rendering. We have this problem in the most of our projects, for example: https://www.avocom.be/. The whole page is white, but when you scroll, it appears again.

It is possible that you do will not see the issue the first time you visit the website. I suggest you to navigate to another page, move you pointer on the menu items and hover, refresh, repeat this until you see the problem. You should see the problem after a few times.

This issue is only visible in Chrome. We found out that it started from version 67.0.3396.87 (Official Build) (64-bit). I already did a bug report to Chrome. In the previous version of Chrome (66.0.3359.181 (Official Build) (64-bit)), the issue did not exist.

We found out that when we stop using Sweet scroll the issue is solved.

Wrong rendering
image

Correct rendering
image

Wrong rendering
image

Correct rendering
image

Optional e.preventDefault() in handleContainerClick function

Hello!

Just included your plugin in a project of mine recenty and I'm finding it really nice! Setup is dead simple and the different easing animations are really good :)

However, I'm looking for a way to override the e.preventDefault() call in .handleContainerClick().

Basically, I've put data-scroll="{id}" attributes to a bunch of <input>s in a large form such that clicking each input scolls to the next question. The scrolling itself is working really well, but now clicking on an input doesn't actually select it. When I removed, e.preventDefault(), everything was fine.

Is there an easy way to remove this behaviour that I haven't seen (or, failing that, would it be possible to add this in)?

Thanks so much!

Paul

Callbacks

Hi tsuyoshiwada,

First of all thanks for the project because it seems amazing.
The problem I am encountering is that I am not able to create callbacks functions for the scroll.
The example option is in typescript but even if I change it to Plain JS and pass the object as the options, it does not work for me.

I just convert this code into JS and the console.log does not execute before the scrolls

`const scroller = new SweetScroll({

// Stop scrolling case of trigger element that contains the is-disabled class.
before: (offset: Offset, $trigger: Element | null, scroller: SweetScroll): boolean | void => {
console.log('Before!!', offset, scroller);
if ($trigger && $trigger.classList.contains('is-disabled')) {
return false;
}
},
});`

I have search Github for code using the callbacks of the sweet-scroll library without any success.
I hope this does not bother as I feel is more my fault than yours but I am in a complete roadblock.

Thanks for everything in advance!

sweet-scroll as react component

Hello @tsuyoshiwada,

thanks for your awesome project. It makes a lot of my work much easier. 💪

I just wonder me if there is a react component or wrapper for sweet-scroll. Because I would like to use it in an react project. would be aweome if there is a way to get it running ;)

Cheers!
Tobias

container is not set when code is compressed with webpack

There is a line in getContainer:

if (!container && !isDomContentLoaded) {

The problem with this line seems to happen when sweet scroll is minified and loads before the DOM is ready.

Like, I think the test for the container needs to be somehow changed so that it fires before the DOM loads, not sure the best way to do that??

Basically if the container is still null but if the DOM has loaded it skips to this line and then silently fails:

callback.call(this, container); // container is null

I think it should be:

if (!container || !isDomContentLoaded) {

If you want to see by running some logs, here is a quick and dirty test to see what I mean:

{
      key: "getContainer",
      value: function getContainer(selector, callback) {
        var _this3 = this;

        var _options = this.options;
        var verticalScroll = _options.verticalScroll;
        var horizontalScroll = _options.horizontalScroll;

        var container = null;

        if (verticalScroll) {
          container = scrollableFind(selector, "y");
        }

        if (horizontalScroll) {
          container = scrollableFind(selector, "x");
        }
        console.log({
          container: container,
          isDomContentLoaded: isDomContentLoaded
        });
        if (!container && !isDomContentLoaded) {
          console.log('no dom content yet');
          (function () {
            var isCompleted = false;

            addEvent(doc, DOM_CONTENT_LOADED, function () {
              isCompleted = true;
              _this3.getContainer(selector, callback);
            });

            // Fallback for DOMContentLoaded
            addEvent(win, "load", function () {
              if (!isCompleted) {
                _this3.getContainer(selector, callback);
              }
            });
          })();
        } else {
          console.log('dom is ready now, container is not always');
          console.log({
            container: container
          })

          callback.call(this, container);
        }
      }

Not installing package, but using url as source

I'm trying to use the plugin in a SharePoint website, and I'm trying to use the plugin in the following form:

require(["https://github.com/tsuyoshiwada/sweet-scroll/blob/master/sweet-scroll.js"], function() {
      var scroller = new SweetScroll({
            vertical: false,
            horizontal: true,
      }, '#container');
})

However, I keep getting an error that SweetScroll is undefined and it's breaking the rest of my script.

If scroll position is 0 on load, initialized is never called...?

At least I think thats whats going on... I tried loading the page with a scroll position greater than 0 and everything works great.

I experienced this in the latest version, so reverted back to 1.0.0, where it works as expected. (Haven't tested the versions inbetween)

Usage with JSX (React.js)

Hi @tsuyoshiwada.
Thanks a lot this awesome library.

When I use the sweet-scroll with JSX, data-scroll attribute is convert to data-scroll="true" so that href variable in below code would be "true". This occurs an incorrect behavior of this.to(distance) method.

const data = el.getAttribute("data-scroll");
const dataOptions = this.parseDataOptions(el);
const href = data || el.getAttribute("href");

Why don't you check whether data-scroll is "true" or URL fragment. 
As an alternative way, I guess specifying the value of data-scroll works well as bellow.

<a href="#feature" data-scroll="#feature">jump</a> <!-- JSX -->

How about adding this usage to README.md.

Fails when container is using scroll-snap

The utility method findScrollable is unable to detect a scrollable container if the container container is using Scroll Snap.

findScrollable tries to scroll by a small amount, and checks that the element is scrolled. If the container is using Scroll Snap the scroll snaps back to 0 and detection fails.

data-scroll属性を指定したヘッダーに、ヘッダーの高さを超える子要素が存在するとき、スクロールの位置がずれる

sweet-scrollをいつも使わせていただいています、使いやすいプラグインをありがとうございます。
初めてのissueで勝手が分からず、既におかしなことをしてしまってます。申し訳ないです。

さて、タイトルの問題についてですが、固定ヘッダーにドロップダウンを実装したところ発生しました。
固定されたヘッダーにdata-scroll属性を指定すると、以下のコードからヘッダーの高さを取得して、高さ分ずれた位置にスクロールされると思います。

var getHeight = function ($el) {
    return Math.max($el.scrollHeight, $el.clientHeight, $el.offsetHeight);
};

その際に、ヘッダーの中でヘッダーの高さを超えた子要素が存在する場合、子要素の高さがスクロールの計算に利用され、見た目上スクロールがおかしな位置で止まったように見える場合があります。これは、scrollHeightの高さが使われているため、発生する問題だと推察します。

そのため、getHeightメソッドで取得する高さはclientHeight、offsetHeightだけで取得するのが良いかと思うのですが、いかがでしょうか。scrollHeightが必要であるケースが想像できなかったので、issueを立てさせてもらいました。

var getHeight = function ($el) {
    return Math.max($el.clientHeight, $el.offsetHeight);
};

以下、今回の現象が再現するコードです。Safari、Chromeの最新版、バージョンは4.0.0で確認しています。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    body {
      margin: 0;
    }
    header {
      position: fixed;
      left: 0;
      top: 0;
      display: flex;
      justify-content: space-between;
      align-items: center;
      width: 100%;
      height: 5rem;
      padding: 0 1rem;
      background: #eee;
      box-sizing: border-box;
    }
    .menu {
      position: relative;
    }
    .menu__child {
      position: absolute;
      right: 0;
      top: 100%;
      width: 5rem;
      height: 10rem;
      background: orange;
      visibility: hidden;
      opacity: 0;
      transition: 1s;
    }
    .menu:hover .menu__child {
      visibility: visible;
      opacity: 1;
    }
    section {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 100%;
      height: 50rem;
    }
    section:first-of-type {
      background: #ddd;
    }
    section:last-of-type {
      background: #ccc;
    }
  </style>
</head>

<body>
  <header data-scroll-header>
    <p>Header</p>
    <div class="menu">
      <p>menu</p>
      <div class="menu__child"></div>
    </div>
  </header>
  <main>
    <section>
      <p>First</p>
      <a href="#second" data-scroll>to Second</a>
    </section>
    <section id="second">
      <p>Second</p>
    </section>
  </main>

  <script src="sweet-scroll.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', () => new SweetScroll(), false);
  </script>
</body>
</html>

スクリーンショット

スクリーンショット 2019-08-14 0 48 18

Scroll not responding

Using the CDN I appear to be following the setup but it the scroll doesn't appear to be responding:

html:

  <body>
	<div id="content">
	
		<div id="map-container">
			<div class="map">
			<nav>
				<ul>
					<li><a href="#one" id="music" class="trigger-overlayx" data-scroll><span class="icon"><img src="assets/img/navigation/MUSIC_ICON.png" /></span>Music</a></li>
					<li><a href="#two" id="tour" class="trigger-overlayx" data-scroll><span class="icon"><img src="assets/img/navigation/TOURING_ICON.png" /></span>Touring</a></li>
					<li><a href="#three" id="video" class="trigger-overlayx" data-scroll><span class="icon"><img src="assets/img/navigation/VIDEO_ICON.png" /></span>Video</a></li>
					<li><a href="#four" id="gallery" class="trigger-overlayx" data-scroll><span class="icon"><img src="assets/img/navigation/PHOTO_ICON.png" /></span>Photo</a></li>
				</ul>
			</nav>
			
			<div class="map-corner" id="one"><img src="assets/img/slices/top_left.jpg" /></div>
			<div class="map-corner" id="two"><img src="assets/img/slices/top_right.jpg" /></div>
			<div class="map-corner" id="three"><img src="assets/img/slices/bottom-left.jpg" /></div>
			<div class="map-corner" id="four"><img src="assets/img/slices/bottom_right.jpg" /></div>
			</div>
		
		</div>
	
	</div>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
	<script src="https://unpkg.com/sweet-scroll/sweet-scroll.min.js"></script>
	<script src="assets/js/main.js"></script>
	<script src="assets/js/overlays.js"></script>
  </body>

js:

document.addEventListener('DOMContentLoaded', () => {
  const sweetScroll = new SweetScroll({ 
		vertical: true,
		horizontal: true
	},'#map-container');
}, false);

I'm not getting any errors. When I select a navigation item the url changes and it jumps.

Offset calculation quirk with expanding navigation menu/header

Sweet Scroll works quite reliably on Desktop, also in horizontal mode. Trouble arises when you use the vertical mode and a navigation (e.g. on mobile) where the navigation expands on click of a navigation button, and obviously hides again after clicking a navigation item.
Even though the header might have a fixed height that doesn't change when navigation pops out, Sweet Scroll uses the height of the content of the specified header element rather than the height of the actual header element. This leads to wrong offsets, especially if you use a full-screen mobile navigation.

How to reproduce:
Have a fixed-height #header (say: 120px) and a button to open the navigation.
Navigation expands to parts or the whole screen after hitting the navigation button, though the height of the container ('#header') remains the same (see Dev Tools).
Click a navigation element, and Sweet Scroll doesn't take into account the header element, but its content, i.e. if the menu spans the whole height, it for some reason stops the viewport height before the actual div, so the element scrolled to isn't visible.

I was able to circumvent it by using a negative offset option using the fixed-header height and setting the 'body' as header option, though it's not too beautiful I assume.

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.