Giter Club home page Giter Club logo

operator's Introduction

operator npm

1.8kb drop-in "PJAX" solution for fluid, smooth transitions between pages. Zero stress.

Features

  1. Advanced routing via matchit
  2. Client-side redirects
  3. Async-by-default, easy data loading between routes
  4. Pages are cached after initial visit
  5. Pre-fetch select pages, as needed

Install

npm i operator --save

Usage

Basically zero config by default, just specify a root DOM node to attach to.

import operator from 'operator'

operator('#root')

Defining routes

To define custom handlers for a given route, pass an object with a path property and handler method.

operator('#root', [
  {
    path: '/',
    handler (state) {
      console.log(state)
    }
  }
])

Routes handlers can also return Promises, and they support params, optional params, and wildcards.

operator('#root', [
  {
    path: '/',
    handler (state) {
      console.log(state)
    }
  },
  {
    path: '/products',
    handler (state) {
      return getProducts() // Promise
    }
  },
  {
    path: '/products/:category/:slug?',
    handler ({ params }) {
      const reqs = [ getProductCategory(params.category) ]
      if (params.slug) reqs.push(getProductBySlug(params.slug))
      return Promise.all(reqs)
    }
  }
])

Route Caching

Routes are cached by default, so on subsequent visits, no data will be loaded. To follow links to pages via AJAX, but fetch fresh content on each navigation action, set cache to false:

operator('#root', [
  {
    'path': '/',
    cache: false
  }
])

Ignoring Routes

Sometimes you need to navigate to a page without AJAX, perhaps to load some sort of iframe content. To do so, set ignore to true:

operator('#root', [
  {
    'path': '/',
    ignore: true
  }
])

Lifecycle

Any function passed to the route config will be called on every route change, kind of like middleware.

const app = operator('#root', [
  state => console.log(state)
])

Operator also emits some helpful events.

app.on('navigate', state => {}) // on valid link click
app.on('before', state => {}) // before render
app.on('after', state => {}) // after render
app.on('hash', state => {}) // when the URL contains a hash

History state

Operator does not manage History or page title, for maximum flexibility to the user. Most people should probably just use this snippet:

app.on('after', ({ previousDocument, location }) => {
  document.title = previousDocumnt.title
  window.history.pushState({}, '', location)
})

If you want to ignore things like query strings or hashes, use pathname:

app.on('after', ({ previousDocumnt, pathname }) => {
  document.title = previousDocumnt.title
  window.history.pushState({}, '', pathname)
})

Hash Anchors

When a hash is encountered – whether on a navigate action between pages, or for scroll-anchors on the same page - Operator will emit a hash event. It's up to you to handle scrolling.

For most sites, this should work:

app.on('hash', ({ hash }) => {
  const target = document.getElementById(hash)

  if (target) {
    const scroll = target.getBoundingClientRect().top + window.pageYOffset
    window.scrollTo(0, scroll)
  }
})

Smooth scrolling is also pretty easy:

import sscroll from 'sscroll'

app.on('hash', ({ hash }) => {
  const target = document.getElementById(hash)
  target && sscroll(target, { duration: 500 })
})

API

go(path)

app.go('/about')

load(path)

Use this for prefetching pages.

app.load('/about')

state (getter)

app.state // => { previousDocument, pathname, location, params, hash, search, handler }

Recipes

Redirects

app.on('before', ({ pathname }) => {
  if (/redirect/.test(pathname)) {
    app.go('/') // redirect
  }
})

Transition animation

import wait from 'w2t'

operator('#root', [
  state => {
    return wait(600, [
      const root = document.documentElement.classList
      return new Promise(res => {
        root.add('is-transitioning')
        setTimeout(() => {
          root.remove('is-transitioning')
          res()
        }, 600)
      })
    ])
  }
])

Changelog

v1.8.0

Removed default scroll handling. This should be moved to user-space in the event the user doesn't want the page to reset to the top.

v1.7.0

Added previousDocument (a complete cloned document object) to the state object. Replaces state.title via previousDocument.title.

v1.6.0

  • Implemented hash event, see docs
  • Fix bad mailto and tel regex, thanks @gabrielloeb!

v1.2.0

Slight update to the API, will require brief migration to new syntax for most users.

  • Deprecated Array format for route configs in favor of more flexible Object syntax
  • Add ignore and cache options

License

MIT License © Eric Bailey

operator's People

Contributors

estrattonbailey avatar gabrielloeb avatar jore 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

Watchers

 avatar  avatar  avatar  avatar

operator's Issues

Route lifecycle

Hi,

I think it would be very useful if the route handler could have an asynchronous lifecycle out of the box.

Greate little lib, by the way, thanks.

cdn link

any cdn link planned :/ ?

(for javascript type module)

Script eval-ing

Should check for src and innerHTML attrs before deciding how to run the script: eval or appendChild()

Nevermind.

Ignore this. Failed to read all the way down the page to the bit about pathname vs location.

Is there a demo site?

Hi

Noob to JS. Is there a demo site that shows operator in action? Looking for a similar use case where a normal site acts like a SPA

Thanks!

Create API to optionally provide custom animations?

Could pass an option with methods as the return value from a route handler.

...
// line 42 index.js
  function render (markup, pathname, customAnimation) {
    const mountNode = document.getElementById(root)
    const oldDom = document
...

Would need to provide something like this

const customAnimation = {
  onBeforeRender () {
    return new Promise((res, rej) => {
      // do work
      res(null)
    })
  },
  onAfterRender () {
    return new Promise((res, rej) => {
      // same
      res(null)
    })
  }
}

IE11

Great library, we would love to use it in production however seems IE11 support is not ideal.

Running into bundling / module issue with matchit / webpack?

Could you suggest anyway around this?

Thanks

Update elements outside of main / restore scroll position on popstate

Hey @estrattonbailey — I'm new to your open source toolset and found you via https://github.com/the-couch/slater/. I really appreciate all the work that went into these goodies! I just have a few questions about operator specifically:

  1. Is there any way to configure it to update body classes, navigation classes, or perhaps anything with a specific data attribute on it that's outside of the main element?
  2. Is there any way to include scroll position restoration on popstate, for example when navigating back to a long collection page from the product page using the browser UI? I consider that pretty crucial for UX as it can be super annoying to find where you were and continue browsing if you're plopped back at the top of the page.

Let me know if you have any thoughts!

Provide option to execute route on initial load or nah

It should probably be optional. You might need to handle transitions and fetch data within the same route. It would be easier to be able to configure both in the same place and just tell operator which to fire when.

Add regex/expression support to routes

Loving the library so far, but an issue that I've encountered while developing some stores is widget and iframe plugins on certain providers like Shopify. Would it be possible to update the ignore feature to match a regex expression?

An example of this would be to ignore all pages of a route like "/blogs/" or "/pages/" where there might be heavy legacy iframe support needed.

Currently for this I'm listing each route individually, but it's starting to get messy. If I could ignore a routes children, I think this could be a nice feature.

Further improve script eval

From MDN:

What if your code is in a JS string? Instead of using eval or innerHTML, both of which trigger synchronous compilation, you should use a Blob with an object URL:

var blob = new Blob([codeString]);
var script = document.createElement('script');
var url = URL.createObjectURL(blob);
script.onload = script.onerror = function() { URL.revokeObjectURL(url); };
script.src = url;
document.body.appendChild(script);

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.