npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@nrk/core-scroll

v4.4.0

Published

> `@nrk/core-scroll` enhances any tag with content to be scrollable with mouse interaction on non-touch-devices. > It also hides the scrollbars and automatically disables animation for users who prefers [reduced motion](https://css-tricks.com/introduction

Downloads

3,220

Maintainers

hammeralfhammeralfjanerikbrjanerikbrthormodbthormodbsiiverssiiverstorsrextorsrexharaldskharaldskeskilgheskilghragnaroh-nrkragnaroh-nrkdaardaldaardalarevjensenarevjensenjulusianjulusianmadsernmadsernandrefauandrefaujfjeldskaarjfjeldskaarmuddahmuddahjensragejensrageoysteinkoppangoysteinkoppangphajsiphajsijorn_georgjorn_georgbjornhelsbjornhelshalvorhhalvorhmorten-nrkmorten-nrknicklassvendsrudnicklassvendsrudkjellvnnrkkjellvnnrksanderknrksanderknrknikolaianikolaiaeirikjstnrkeirikjstnrkcarinafraningcarinafraninghelenperhelenperstefanogdennrkstefanogdennrkjimmeloysundjimmeloysundtobiasrptobiasrpmartioskmartioskjimalexbergerjimalexbergergunderwondergunderwonderhamnishamnisluminrkluminrksupermeisensupermeisenvagifabilovvagifabilovclaudio-nrkclaudio-nrkhaakemonhaakemonzenangstzenangstrannveigncrannveignceschoieneschoienbaltebaltetoshbtoshbemte123emte123opetopetklizterkliztermikkelnygardmikkelnygardfeiringfeiringdervodevdervodevgrimburgrimburgardkroyergardkroyerkariaankariaanedplayzedplayzelias-chairielias-chairimiatollaksvikmiatollaksvikytterboytterbomachineboycommachineboycomtrulsltrulslmslhmmslhmcbjerkancbjerkanhermangudesenhermangudesenandreeldareideandreeldareidehenningkollerhenningkollerespenhalstensenespenhalstensendanjohnrkdanjohnrkolapeterolapeterteodor-elstadteodor-elstadlorecasterlorecasternrk-ps-teamcitynrk-ps-teamcityswlaswlanrk-midas-jenkinsnrk-midas-jenkinsandorpandorandorpandornrkrichardnrkrichardgesigesigundelsby-nrkgundelsby-nrkjonstalecarlsenjonstalecarlsennrk-sofie-cinrk-sofie-cinytaminnytaminjesperstarkarjesperstarkarskjalgepalgskjalgepalgeirikhalvardeirikhalvardastokkeastokken640071n640071n07073n07073henrik-mattssonhenrik-mattssonhaavardmhaavardmyryrnrk-kurator-jenkinsnrk-kurator-jenkinstorgeilotorgeilonrk-user-syncnrk-user-syncdhdeploydhdeployespenwaespenwaovstetunovstetunstianljstianljharaldkjharaldkjmariusumariusucristobalcristobalknuthaugknuthaugthohalvthohalvjohnarnejohnarneeshaswinieshaswinimorrowmorrowoyvindehoyvindehlaatlaattoggutoggunrk-jenkinsnrk-jenkinsplommaplommaevjandevjandmoltubakkmoltubakkingridgureningridgurenlu-luxlu-luxanderslianderslisiljesiljestiandgstiandgsjurlursjurlurandipodnrkandipodnrkpkejpkejyosrimtiyosrimtimorten.nyhaugmorten.nyhaugingvildcathingvildcatherlend.joneserlend.jonesbrneirikbrneirikmollersemollersetbnrktbnrknordankenordankesimonmitternachtsimonmitternachtmartintorgersenmartintorgersenrebchrrebchrsteipalsteipaldiscobusdiscobusmartingundersenmartingundersentinkajtstinkajtshallvardlidhallvardlidtomivartomivarajacoajacotobinustobinusmortenokmortenoknrk-ark-deploynrk-ark-deployjeangilbertlouisjeangilbertlouisheidimorkheidimorkingriddraageningriddraagenfridajalborgfridajalborgbruusibruusirosvollrosvollchristianeidechristianeideenordbyenordbyglen_imrieglen_imriemia.aasbakkenmia.aasbakkenelathamnaelathamnaevjjan17evjjan17olatoftolatoftkongsrudkongsrudchrpeterchrpeteringvildforsethingvildforsethharaldk76haraldk76stigokstigokjohannesodlandjohannesodlandanders993anders993vildefjvildefjvildepkvildepkrolerbolerrolerbolermeloyguttmeloyguttanders.baggethunanders.baggethun

Keywords

Readme

Core Scroll

@nrk/core-scroll enhances any tag with content to be scrollable with mouse interaction on non-touch-devices. It also hides the scrollbars and automatically disables animation for users who prefers reduced motion.

Example

<!--demo-->
<button data-for="my-scroll-js" value="up" aria-label="Rull opp">&uarr;</button>
<button data-for="my-scroll-js" value="down" aria-label="Rull ned">&darr;</button>
<br>
<button data-for="my-scroll-js" value="left" aria-label="Rull til venstre">&larr;</button>
<button data-for="my-scroll-js" value="right" aria-label="Rull til høyre">&rarr;</button>
<div class="my-wrap my-wrap-js">
  <core-scroll id="my-scroll-js" class="my-scroll">
    <div>1</div><div>2</div><div>3</div><div>4</div><a href="#">5</a>
    <div>6</div><div>7</div><div>8</div><div>9</div><div>10</div>
    <div>11</div><div>12</div><div>13</div><div>14</div><div>15</div>
    <br>
    <div>1</div><div><div class="my-wrap">
      <core-scroll class="my-scroll">
        <div>1</div><div>2</div><div>3</div><div>4</div><a href="#">5</a>
        <div>6</div><div>7</div><div>8</div><div>9</div><div>10</div>
        <div>11</div><div>12</div><div>13</div><div>14</div><div>15</div>
        <br>
        <div>1</div><div>2</div><div>3</div><div>4</div><a href="#">5</a>
        <div>6</div><div>7</div><div>8</div><div>9</div><div>10</div>
        <div>11</div><div>12</div><div>13</div><div>14</div><div>15</div>
        <br>
        <div>1</div><div>2</div><div>3</div><div>4</div><a href="#">5</a>
        <div>6</div><div>7</div><div>8</div><div>9</div><div>10</div>
        <div>11</div><div>12</div><div>13</div><div>14</div><div>15</div>
      </core-scroll>
    </div></div><div>3</div><div>4</div><a href="#">5</a>
    <div>6</div><div>7</div><div>8</div><div>9</div><div>10</div>
    <div>11</div><div>12</div><div>13</div><div>14</div><div>15</div>
    <br>
    <div>1</div><div>2</div><div>3</div><div>4</div><a href="#">5</a>
    <div>6</div><div>7</div><div>8</div><div>9</div><div>10</div>
    <div>11</div><div>12</div><div>13</div><div>14</div><div>15</div>
    <br>
    <div>1</div><div>2</div><div>3</div><div>4</div><a href="#">5</a>
    <div>6</div><div>7</div><div>8</div><div>9</div><div>10</div>
    <div>11</div><div>12</div><div>13</div><div>14</div><div>15</div>
  </core-scroll>
</div>

Example: React

<!--demo-->
<div id="jsx-scroll"></div>
<script type="text/JavaScript">
  class MyScroll extends React.Component {
    constructor (props) {
      super(props)
      this.state = {}
      this.onScroll = this.onScroll.bind(this)
    }
    onScroll ({target}) {
      this.setState({
        left: target.scrollLeft ? () => target.scroll('left') : null,
        right: target.scrollRight ? () => target.scroll('right') : null
      })
    }
    render () {
      return <div>
        <button disabled={!this.state.left} onClick={this.state.left}>Left JSX</button>
        <button disabled={!this.state.right} onClick={this.state.right}>Right JSX</button>
        <div className="my-wrap">
          <CoreScroll className="my-scroll" onScrollChange={this.onScroll}>
            <div>1</div><div>2</div><div>3</div><div>4</div><a href="#">5</a>
            <div>6</div><div>7</div><div>8</div><div>9</div><div>10</div>
            <div>11</div><div>12</div><div>13</div><div>14</div><div>15</div>
          </CoreScroll>
        </div>
      </div>
    }
  }
  ReactDOM.render(<MyScroll />, document.getElementById('jsx-scroll'))
</script>

Example: custom child items

core-scroll calculates scroll distance based on currently visible direct children. When using substructures like <ul><li>... you, must tell core-scroll what elements are considered items, by using the items attribute/property:

<core-scroll items="li">
  <ul>
    <li>List-item 1</li>
    <li>List-item 2</li>
    ...
  </ul>
</core-scroll>
<!--demo-->
<button data-for="my-scroll-child" value="left" aria-label="Rull til venstre">&larr;</button>
<button data-for="my-scroll-child" value="right" aria-label="Rull til høyre">&rarr;</button>
<div class="my-wrap">
  <core-scroll id="my-scroll-child" class="my-scroll" items="li">
    <ul>
      <li>List-item 1</li><li>List-item 2</li><li>List-item 3</li><li>List-item 4</li>
      <li>List-item 5</li><li>List-item 6</li><li>List-item 7</li><li>List-item 8</li>
      <li>List-item 9</li><li>List-item 10</li><li>List-item 11</li><li>List-item 12</li>
    </ul>
  </core-scroll>
</div>

Example: changes in DOM

Core scroll uses a MutationObserver to monitor changes to childnodes. Connected buttons are updated (disabled or not) if their viability changes as a result of the DOM-change

<!--demo-->
<div id="jsx-dynamic-content"></div>
<script type="text/JavaScript">
  const Dynamic = () => {
      const [elements, setElements] = React.useState([...Array(10).keys()])
      const content = elements.map(item => <div> Element {item + 1}</div>);
      return (
        <>
          <button type="button" onClick={() => setElements([...elements, elements.length])}>
            Add extra child
          </button>
          <button type="button" onClick={() => setElements([...Array(10).keys()])}>
            Set to ten children
          </button>
          <button type="button" onClick={() => setElements([])}>
            Remove all children
          </button>
          <br />
          <button type="button" data-for="scroll-dynamic-content" value="left" aria-label="Rull til venstre">&larr;</button>
          <button type="button" data-for="scroll-dynamic-content" value="right" aria-label="Rull til høyre">&rarr;</button>
          <div className="my-wrap">
            <CoreScroll id="scroll-dynamic-content" className="my-scroll">
              {content}
            </CoreScroll>
          </div>
        </>
      )
    }
  ReactDOM.render(<Dynamic />, document.getElementById('jsx-dynamic-content'))
</script>

Installation

Using NPM provides own element namespace and extensibility. Recommended:

npm install @nrk/core-scroll  # Using NPM

Using static registers the custom element with default name automatically:

<script src="https://static.nrk.no/core-components/major/10/core-scroll/core-scroll.min.js"></script>  <!-- Using static -->

Remember to polyfill custom elements if needed.

Usage

Buttons should be connected to a core-scroll element to control scrolling for keyboard-users. Just add the data-for attribute with the id of core-scroll and assign a directional value. The disabled attribute is then automatically toggled when there is or isn't space to scroll in the assigned direction.

Note: core-scroll adds styling to hide scrollbars, see how to work with margin and height →.

<button
  data-for="my-scroll-js"      <!-- {String} Id of <core-scroll> -->
  value="up"              <!-- {String} Sets direction of scroll. Possible values: "left", "right", "up" or "down" -->
  aria-label="Rull opp">  <!-- {String} Sets label -->
  &uarr;
</button>
<core-scroll
  id="my-scroll-js"       <!-- {String} Id corresponding to for attribute of <button> -->
  friction=".2">          <!-- {Number} Optional. Default 0.8. Controls scroll speed. Lower friction means higher speed -->
  <div>1</div>            <!-- Direct children is used to calculate natural stop points for scroll -->
  <div>2</div>
  <div>3</div>
</core-scroll>
import CoreScroll from '@nrk/core-scroll'                 // Using NPM
window.customElements.define('core-scroll', CoreScroll)   // Using NPM. Replace 'core-scroll' with 'my-scroll' to namespace

const myScroll = document.querySelector('core-scroll')

// Getters
myScroll.scrollLeft                   // Amount of pixels remaining in scroll direction left
myScroll.scrollRight                  // Amount of pixels remaining in scroll direction right
myScroll.scrollTop                    // Amount of pixels remaining in scroll direction up
myScroll.scrollBottom                 // Amount of pixels remaining in scroll direction down
myScroll.items                        // Get all items

// Setters
myScroll.items                        // Set to String to specify scroll children (see example above)

// Methods
myScroll.scroll('left')                             // Scroll in specified direction
myScroll.scroll({x: 0, y: 10})                      // Scroll to exact position
myScroll.scroll({x: 0, move: 'down'})               // Scroll with position and direction
myScroll.scroll(document.getElementById('childId')) // Scroll to child element, centered inside scroll-area

React / Preact

import CoreScroll from '@nrk/core-scroll/jsx'

<CoreScroll friction={Number}             // Optional. Default 0.8. Controls scroll speed
            ref={(comp) => {}}            // Optional. Get reference to React component
            forwardRef={(el) => {}}       // Optional. Get reference to underlying DOM custom element
            onScrollChange={Function}     // Optional. Scroll change event handler
            onScrollClick={Function}      // Optional. Scroll click event handler
>
  {/* elements */}
</CoreScroll>

Events

Note: Starting a core-scroll mousemove inside a iframe, and releasing the mouse outside, will result in an incomplete action and fail to end movement. This is due to mouseup not bubbling though iframes. Please avoid iframes.

scroll.change

Fired regularly during a scroll. The event is throttled to run every 500ms and ensure better performance:

document.addEventListener('scroll.change', (event) => {
  event.target   // The scroll element
})

scroll.click

Fired when clicking a button controlling core-scroll:

document.addEventListener('scroll.click', (event) => {
  event.target        // The scroll element
  event.detail.move   // Direction to move (left, right, up, down)
})

scroll

A native event fired for every scrolled pixel. Be cautious about performance when listening to scroll; heavy or many read/write operations will slow down your page. The event does not bubble, and you therefore need useCapture set to true when listening for scroll events from a parent element:

document.addEventListener('scroll', (event) => {
  event.target        // NB: Can be any scrolling element since this is a native event

  // Example check if the event.target is the correct @nrk/core-scroll
  if (event.target.id === 'ID-OF-MY-CORE-SCROLL-HERE') {
    // Do Something
  }
}, true) // Note the true parameter, activating capture listening

Styling

Scrollbar hiding

core-scroll adds negative margins in some browsers to hide scrollbars. Therefore, make sure to place core-scroll inside a wrapper element with overflow: hidden:

<div style="overflow:hidden"><core-scroll>...</core-scroll></div>

Setting height

By default, core-scroll scales based on content. If you want to set a fixed height, set this on the wrapper element (not directly on the core-scroll element):

✅ Do | 🚫 Don't :-- | :-- <div style="overflow:hidden;height:200px"><core-scroll>...</core-scroll></div> | <div style="overflow:hidden"><core-scroll style="height:200px"></core-scroll></div>

Button states

The <button> elements receive disabled attributes reflecting the current scroll state:

.my-scroll-button {}                  /* Target button in any state */
.my-scroll-button:disabled {}         /* Target button in disabled state */
.my-scroll-button:not(:disabled) {}   /* Target button in enabled state */

NB: Safari scrollbar bug

If you are creating a horizontal layout, you might experience unwanted vertical scrolling in Safari. This happens when children of @nrk/core-scroll have half-pixel height values (due to images/videos/elements with aspect-ratio sizing). Avoid the vertical scrolling by setting padding-bottom: 1px on the @nrk/core-scroll element.

NB: iOS 12.2+ bug

core-scroll automatically adds -webkit-overflow-scrolling: touch as this is required by iOS to enable momentum scrolling. An unfortunate side effect (introduced in iOS 12.2) is that the scrollable area is rendered on the GPU, which breaks position: fixed on child elements. Please place elements with position: fixed (i.e. a <core-dialog>) outside the markup of <core-scroll>.