Code Monkey home page Code Monkey logo

page-lifecycle's Introduction

PageLifecycle.js

Overview

PageLifecycle.js is a tiny JavaScript library (<1K gzipped) that allows developers to easily observe Page Lifecycle API state changes and implement Page Lifecycle best practices consistently across all browsers.

▶️ View demo: page-lifecycle.glitch.me 👀

Installation

You can install this library from npm by running:

npm install --save-dev page-lifecycle

Usage

Releases of this library include three minified, production-ready versions:

1. ES5: dist/lifecycle.es5.js (UMD) ⭐

Use this version for maximum compatibility with legacy browsers (that can't run ES2015+ code).

As a UMD bundle, it can be required in CommonJS or AMD environments, or it can be loaded with a script tag as the browser global lifecycle.

<script defer src="/path/to/lifecycle.es5.js"></script>
<script defer>
lifecycle.addEventListener('statechange', function(event) {
  console.log(event.oldState, event.newState);
});
</script>

2. ES2015: dist/lifecycle.mjs (ESM) 🔥

Use this version if you only support ES module-capable browsers or if you're using <script type="module"> and the nomodule fallback to conditionally target modern browsers.

<script type="module">
import lifecycle from '/path/to/page-lifecycle.mjs';

lifecycle.addEventListener('statechange', function(event) {
  console.log(event.oldState, event.newState);
});
</script>

3. ES2015 (native): dist/lifecycle.native.mjs (ESM w/o EventTarget and Event shims) ⚠️

Use this version if you're only targeting browsers that support extending EventTarget and Event constructors.

Note: this version is the smallest but will only work in some browsers. The implementation instructions are the same as the ES2015 version above.

API

The PageLifecycle.js library exports a lifecycle object, which is a singleton instance of the Lifecycle class. The Lifecycle class has the following properties, methods, and events:

Properties

Name Type Description
state string Returns the current Page Lifecycle state.
pageWasDiscarded boolean Returns the value of document.wasDiscarded (or false if not present).

Methods

Name Description
addEventListener

Parameters:

  • type: string
  • listener: function(Event)

Adds a callback function to be invoked whenever the passed event type is detected. (Note: at the moment only the "statechange" event type is supported.)

removeEventListener

Parameters:

  • type: string
  • listener: function(Event)

Removes a function from the current list of listeners for the passed event type. (Note: at the moment only the "statechange" event type is supported.)

addUnsavedChanges

Parameters:

  • id: Object|Symbol

Adds an item to an internal pending-changes stack. Calling this method adds a generic beforeunload listener to the window (if one isn't already added).

The argument passed should be unique to this state, as it can only be removed by passing the same argument to removeUnsavedChanges().

removeUnsavedChanges

Parameters:

  • id: Object|Symbol

Removes an item matching the passed argument from an internal pending-changes stack. If the stack is empty, the generic beforeunload listener is removed from the window.

Events

Name Description
statechange

Properties:

  • newState: string The current lifecycle state the page just transitioned to.
  • oldState: string The previous lifecycle state the page just transitioned from.
  • originalEvent: Event the DOM event that triggered the state change.

The statechange event is fired whenever the page's lifecycle state changes.

Browser Support

Chrome
Firefox
Safari
Edge
Internet Explorer
9+
Opera

PageLifecycle.js has been tested and known to work in the above browsers.

page-lifecycle's People

Contributors

mathiasbynens avatar philipwalton avatar samouss 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

page-lifecycle's Issues

Can not work with iframe?

I write a demo. codesandbox

question:

When I focus in parent page, and then focus out parent page. The console will log the statechange event.

But When I focus in iframe page's input, and then focus out input. There is no statechage event.

beforeunload not working in iOS browsers

I am trying to display the "Are you sure you want to leave" prompt using the beforeunload event. However, it does not work in iOS, neither in Safari nor in Chrome. I am using the addUnsavedChanges method. Am I missing something?

Handle cases where Safari doesn't dispatch the pagehide or visibilitychange events

On desktop Safari, when closing a tab by clicking the (X) on the tab UI itself, no visibilitychange or pagehide event is dispatched, so the page becomes terminated but the reported state never transitions away from active.

In order to at least provide a guarantee of hidden, we should listen for the beforeunload event and report a change to hidden if no event handlers set a returnValue or call preventDefault().

The following code achieves this:

addEventListener('beforeunload', (evt) => {
  setTimeout(() => {
    if (!(evt.defaultPrevented || evt.returnValue)) {
      // The state is now going to either FROZEN or TERMINATED.
    }
  }, 0);
});

But since adding an unconditional beforeunloaded handler will break the page navigation cache in Firefox, we should only add this logic in Safari.

Documentation for new- and oldState values

The README doesn't mention what values we can expect for the oldState and newState arguments from the statechange event. I can check the code and/or experiment with the demo to find out but it would be nice if this was documented.

SSR / pre-rendering: document is undefined

Description

I had some trouble using this library with preactjs/wmr, specifically when I tried to prerender the project.

At first, I didn't understand what went wrong as I had a check for typeof(document) in place before trying to do anything with the imported lifecycle library.

Then I had a look at the source code and realized page-lifecycle's export is a new Lifecycle() instance. In other words, just importing the library causes it execute code and hit a document.visibilityState statement, causing the error in environments that don't have a document object - like preactjs/wmr's prerender step.

Reproduce:

In a preactjs/wmr project, create a component and statically import the page-lifecycle library, then try to build with wmr --prerender

(Disclaimer: untested/pseudo code)

import lifecycle from 'page-lifecycle';

export default function Component() {
  if ( typeof(document) !== 'undefined) ) console.log( lifecycle.state );
  return <div />;
}
npx wmr build --prerender

Throws an error (sorry for the minified code):

ReferenceError: document is not defined
> 1 | …(((e,n,t)=>(e[n]=t,e)),{}))),yn=()=>document.visibilityState===un?un:docu…

Workaround:

Import src/Lifecycle.mjs directly and instantiate the exported class manually:

import Lifecycle from 'page-lifecycle/src/Lifecycle.mjs';

export default function Component() {
  if ( typeof(document) !== 'undefined) ) {
    const lifecycle = new Lifecycle();
    console.log( lifecycle.state );
  }
  return <div />;
}
npx wmr build --prerender

Syntax error in IE (non ES5 code in package)

The convention for package.json fields is

  • main: code with CommonJS exports which is otherwise compatible with target platforms
  • module: code with ES6/harmony exports which is otherwise compatible with target platforms

This package breaks that convention by making the module field point directly to source code which is not compatible with the target platforms. Specifically, even though the readme sais that this package supports IE, in a typical webpack setup (where node_modules is not transpiled with babel), it results in a syntax error on IE (because of e.g. the arrow functions).

So I think either:

  • the module field should be removed, or renamed to esnext or some such thing (not sure if there is an accepted standard for code that should be transpiled by the user)
  • the module field should point not to the original source but to a built version of it which contains ES6/harmony exports, but is otherwise fully transpiled such that it works on all supported browsers.

Typescript Definitions

Hello,

Thank you very much for the library. It's great.

Would it be possible for you to add typescript definitions for the library?

Have a great week.

addUnsavedChanges() will save same id multiple times

addUnsavedChanges() was intended to avoid saving the same id multiple times (the comment says "Don't add duplicate state"), but it still does. This means that multiple calls to addUnsavedChanges() with a given id followed by one call to removeUnsavedChanges() with that same id will leave other copies of the id in the array, and the beforeunload listener will still be active.

The issue is that the condition here is always true.

Instead of !indexOf(id) > -1 it should be !(indexOf(id) > -1), I believe.

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.