Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A swup plugin for dynamically replacing containers based on rules 🧩

License

NotificationsYou must be signed in to change notification settings

swup/fragment-plugin

Repository files navigation

Unit TestsE2E TestsLicense

Aswup plugin for dynamically replacing containers based on rules 🧩

  • Selectively replace containers instead of the main swup containers, based on custom rules
  • Improve orientation by animating only the parts of the page that have actually changed
  • Give your site the polish and snappiness of a single-page app

Use cases

All of the following scenarios require updating only a small content fragment instead ofperforming a full page transition:

  • a filter UI that live-updates its list of results on every interaction
  • a detail overlay that shows on top of the currently open content
  • a tab group that should update only itself when selecting one of the tabs

If you are looking for selectively replacing forms on submission, you should have a look atForms Plugin.

Demo

See the plugin in action inthis interactive demo

swup-fragment-plugin.mp4

Table of contents

Installation

Install the plugin from npm and import it into your bundle.

npm install @swup/fragment-plugin
importSwupFragmentPluginfrom'@swup/fragment-plugin';

Or include the minified production file from a CDN:

<scriptsrc="https://unpkg.com/@swup/fragment-plugin@1"></script>

How it works

When a visit is determined to be a fragment visit, the plugin will:

  • update only the contents of the elements matching the rule'scontainers
  • not update the defaultcontainers replaced on all other visits
  • wait for CSS transitions on those fragment elements usingscoped animations
  • preserve the current scroll position upon navigation
  • add ato-[name] class to the elements if the currentrule has aname key
  • ignore elements that already match the current visit's URL

Example

Content filter: only update a list of results

Imagine a website with a/users/ page that displays a list of users. Above the user list, thereis a filter UI to choose which users to display. Selecting a filter will trigger a visitto the narrowed-down user list at/users/filter/x/. The only part that has changed is thelist of users, so that's what we'd like to replace and animate instead of the whole contentcontainer.

<body><header>Website</header><mainid="swup"class="transition-main"class="transition-main"><h1>Users</h1><!-- A list of filters for the users: selecting one will update the list below --><ul><ahref="/users/filter/1/">Filter 1</a><ahref="/users/filter/2/">Filter 2</a><ahref="/users/filter/3/">Filter 2</a></ul><!-- The list of users, filtered by the criteria above --><ulid="users"><li><ahref="/user/1/">User 1</a></li><li><ahref="/user/2/">User 2</a></li><li><ahref="/user/3/">User 3</a></li></ul></main></body>

Using Fragment Plugin, we can updateonly the#users list when clicking one of the filters.The plugin expects an array of rules to recognize and handle fragment visits:

constswup=newSwup({plugins:[newSwupFragmentPlugin({rules:[{from:'/users/:filter?',to:'/users/:filter?',containers:['#users']}]})]});

Now we can add custom animations for our fragment rule:

/** The default animation, for visits without matching fragment rules*/html.is-changing .transition-main {transition: opacity250ms;opacity:1;}html.is-animating .transition-main {opacity:0;}/** The animation when filtering users*/#users.is-changing {transition: opacity250ms;}#users.is-animating {opacity:0;}

Options

/** A path to match URLs against */typePath=string|RegExp|Array<string|RegExp>;/** A fragment rule */exporttypeRule={from:Path;to:Path;containers:string[];name?:string;scroll?:boolean|string;focus?:boolean|string;if?:Predicate;};/** The plugin options */exporttypeOptions={rules:Rule[];debug?:boolean;};

rules

The rules that define whether a visit will be considered a fragment visit. Each rule consists ofmandatoryfrom andto URL paths, an array of selectorscontainers, as well as an optionalname of this rule to allow scoped styling.

The rule'sfrom/to paths are converted to a regular expression bypath-to-regexp and matched against the current browser URL. If you want to create an either/or path, you can also provide an array of paths, for example:

{  rules:[{from:['/users','/users/:filter?'],to:['/users','/users/:filter?'],containers:['#user-list']}];}

rule.from

Required, Type:string | string[] – The path(s) to match against the previous URL

rule.to

Required, Type:string | string[] – The path(s) to match against the next URL

rule.containers

Required, Type:string[] – Selectors of containers to be replaced if the visit matches.

Notes

  • only IDs and no nested selectors are allowed.#my-element is valid, while.my-element or#wrap #child both will throw an error.
  • ifany of the selectors incontainers doesn't return a match in the current document, the rule will be skipped.
  • Fragment elementsmust either match a swup container or be a descendant of one of them

rule.name

Optional, Type:string – A name for this rule to allow scoped styling, ideally inkebab-case

rule.scroll

Optional, Type:boolean | string – By default, scrolling will be disabled for fragment visits.Using this option, you can re-enable it for selected visits:

  • true will scroll to the top
  • '#my-element' will scroll to the first element matching the selector

rule.focus

Optional, Type:boolean | string – If you haveAccessibility Plugin installed, you can adjust which element to focus for the visitas described here.

rule.if

Optional, Type:(visit) => boolean – A predicate function that allows for fine-grained control over the matching behavior of a rule. The function receives the currentvisit as a parameter. If the function returnsfalse, the related rule is being skipped for the current visit, even if it matches the current route.

debug

Optional, Type:boolean, Defaultfalse. Set totrue for debug information in the console.

{  debug:true;}

[!IMPORTANT] to keep the bundle size small, UMD builds are stripped from all debug messages, sodebug won't have an effect there.

How rules are matched

  • The first matching rule in yourrules array will be used for the current visit
  • If no rule matches the current visit, the default content containers defined in swup's options will be replaced

How fragment containers are found

  • Thecontainers of the matching ruleneed to be shared between the current and the incoming document
  • For each selector in thecontainers array, thefirst matching element in the DOM will be selected
  • If a visit isn't be considered a reload of the current page, fragment elements that already match the new URL will be ignored

Advanced use cases

Creating the rules for your fragment visits should be enough to enable dynamic updates on mostsites. However, there are some advanced use cases that require adding certain attributes to thefragment elements themselves or to links on the page. These tend to be situations wheremodal dialogs are involved and swup doesn't know which page the modal was opened from.

Fragment URL

Use thedata-swup-fragment-url attribute to uniquely identify fragment elements.

In scenarios where a modal is rendered on top of other content, leaving or closing the modal tothe same URL it was opened from should ideally not update the content behind it asnothing has changed. Fragment plugin will normally do that by keeping track of URLs. However,when swup was initialized on a subpage with an already-visible modal, the plugin doesn't know which URL the content behind it corresponds to. Hence, we need to tell swup manually so it can persist content when closing the modal.

<!-- the modal --><dialog open>  <main>    <h1>User 1</h1>    <p>Lorem ipsum dolor sit amet...</p>  </main></dialog><!-- the content behind the modal --><main>  <section+   data-swup-fragment-url="/users/"    >    <ul>      <li>User 1</li>      <li>User 2</li>      <li>User 3</li>    </ul>  </section></main>

Link to another fragment

Use thedata-swup-link-to-fragment attribute to automatically update links pointing to a fragment.

Consider again an overlay rendered on top of other content. To implement a close button for thatoverlay, we could ideally point a link at the URL of the content where the overlay is closed. Thefragment plugin will then handle the animation and replacing of the overlay. However, knowingwhere to point that link requires knowing where the current overlay was opened from.

data-swup-link-to-fragment automates that by keeping thehref attribute of a link in sync with the currentlytracked URL of the fragment matching the selector provided by the attribute. The code below will make sure the close button will always point at the last known URL of the#list fragment to allow seamlessly closing the overlay:

<dialog open>  <main>    <!-- `href` will be synced to the fragment URL of #list at runtime: -->+   <a href="" data-swup-link-to-fragment="#list">Close</a>    <h1>User 1</h1>    <p>Lorem ipsum dolor sit amet...</p>  </main></dialog><main>  <section    data-swup-fragment-url="/users/">    <ul>      <li>User 1</li>      <li>User 2</li>      <li>User 3</li>    </ul>  </section></main>

Tip

To keep your markup semantic and accessible, we recommend youalways provide a default valuefor the link'shref attribute, even though it will be updated automatically at runtime:

<a+ href="/users/"  data-swup-link-to-fragment="#list">Close</a>

Skip animations using<template>

If all elements of a visit are<template> elements, theout/in-animation will automatically be skipped. This can come in handy for modals:

{from:'/overview/',to:'/detail/:id',containers:['#modal']},{from:'/detail/:id',to:'/overview/',containers:['#modal']}
<!-- /overview/: provide a <template> as a placeholder for the modal --><templateid="modal"></template><main><ul><!-- list of items that will open in the #modal --></ul></main>
<!-- /detail/1 --><dialogopenid="modal"><main><h1>Detail 1</h1></main></dialog><main><ul><!-- list of items that will open in the #modal --></ul></main>

Tip

Fragment Plugin will detect<dialog open>-fragment elements automatically on every page view andmove them to thetop layerautomatically. This has the benefit of simplified accesssiblity handling and styling.

API methods

Fragment plugin provides a few API functions for advanced use cases. To be able to access those, you'll need to keep a reference to the plugin during instanciation:

constfragmentPlugin=newSwupFragmentPlugin({ rules});constswup=newSwup({plugins:[fragmentPlugin]});/** You can now call the plugin's public API, for example: */fragmentPlugin.getFragmentVisit(route);

getFragmentVisit(route)

Get information about the fragment visit for a given route. Returns eitherFragmentVisit orundefined.

/** * Get information about which containers will * be replaced when hovering over links: */document.querySelectorAll('a[href]').forEach((el)=>{el.addEventListener('mouseenter',()=>{constfragmentVisit=fragmentPlugin.getFragmentVisit({from:window.location.href,// the current URLto:el.href// the URL of the link});console.log(`will replace${fragmentVisit?.containers||swup.options.containers}`);});});

prependRule(rule)

Prepends arule to the array of rules.

fragmentPlugin.prependRule({from:'/foo/',to:'/bar/',containers:['#foobar']});

appendRule(rule)

Appends arule to the array of rules.

fragmentPlugin.prependRule({from:'/baz/',to:'/bat/',containers:['#bazbat']});

getRules()

Get aclone of all registered fragment rules

console.log(fragmentPlugin.getRules());

setRules(rules)

Overwrite all fragment rules with the provided rules. This methods provides the lowest-level access to the rules. For example, you could use it to remove all rules with the namefoobar:

fragmentPlugin.setRules(fragmentPlugin.getRules().filter((rule)=>rule.name!=='foobar'));

[8]ページ先頭

©2009-2025 Movatter.jp