Pull To Refresh

Pull to refresh is a special component that can be used to initiate the refreshing of a page’s contents.

Pull To Refresh Layout

Let's look on how to integrate pull to refresh to page:

<div class="page">
  <!-- Page content must have additional "ptr-content" class -->
  <div class="page-content ptr-content" data-ptr-distance="55" data-ptr-mousewheel="true">
    <!-- Default pull to refresh preloader-->
    <div class="ptr-preloader">
      <div class="preloader"></div>
      <div class="ptr-arrow"></div>
    </div>

    <!-- usual content below -->
    <div class="list">
      ...
    </div>

    <!-- nested scrollable element -->
    <div class="my-scolling-content ptr-watch-scrollable">
      ...
    </div>

    <!-- another nested scrollable element -->
    <div class="another-scolling-content ptr-ignore">
      ...
    </div>
  </div>
</div>

Where:

  • ptr-content class. This is required to enable pull to refresh
  • div class="ptr-preloader" hidden layer with pull to refresh visual elements: preloader and arrow
  • data-ptr-distance="55" additional attribute that allows to set custom pull to refresh trigger distance. By default (if not specified) it is 44px
  • data-ptr-mousewheel="true" additional attribute that enables pull to refresh with mousewheel (for desktop apps). Works only for PTR top.
  • ptr-watch-scrollable - additional class that must be added on nested scrollable elements to prevent pull to refresh on such elements scrolling
  • ptr-ignore - additional class that must be added on nested scrollable elements or other elements that will to prevent pull to refresh on such elements scrolling or touchmove

Pull To Refresh From Bottom

It is also possible to make it work like pull from bottom. In this case we need to move ptr-preloader element to the bottom of page content and add additional ptr-bottom class to pull to refresh content:

<div class="page">
  <!-- ptr-content must have additional "ptr-bottom" class -->
  <div class="page-content ptr-content ptr-bottom">
    <div class="list">
      ...
    </div>

    <!-- Pull to refresh preloader moves to bottom -->
    <div class="ptr-preloader">
      <div class="preloader"></div>
      <div class="ptr-arrow"></div>
    </div>
  </div>
</div>

Pull To Refresh Sequence

When user starts to pull ptr-content down, then ptr-preloader will receive additional ptr-pull-down class.

When user pulls down ptr-content on a distance more than 44px (when ptr-preloader will be fully visible), then ptr-preloader will receive additional ptr-pull-up class which changes arrow rotation to notify user about refresh action on release.

When user release pull to refresh content when it is in "ptr-pull-up" state, then ptr-preloader will receive additional ptr-refreshing class. In "refreshing" state arrow will be hidden and user will see preloader indicator. On this stage you probably need to do Ajax request and refresh page content.

Pull To Refresh App Methods

There are few App's methods that can be used with pull to refresh container:

app.ptr.create(el)- initialise PTR on specified HTML element container.

  • el - HTMLElement or string (with CSS selector) - PTR element (ptr-content). Required.

Method returns created PTR instance

Use this method only in case you have added ptr content after page init or if you want to enable it later. Otherwise if there is "ptr-content" element on page init it will be created automatically

app.ptr.destroy(el)- remove PTR event listeners from the specified HTML element

  • el - HTMLElement or string (with CSS selector) - PTR element (ptr-content). Required.

app.ptr.get(el)- get PTR instance by HTML element

  • el - HTMLElement or string (with CSS Selector). PTR element (ptr-content).

Method returns PTR instance

app.ptr.done(el)- reset PTR state on specified PTR content element

  • el - HTMLElement or string (with CSS Selector). PTR element (ptr-content). Required.

app.ptr.refresh(el)- trigger PTR on specified PTR content element

  • el - HTMLElement or string (with CSS Selector). PTR element (ptr-content). Required.

Pull To Refresh Methods & Properties

If we created PTR manually or used app.ptr.get method we will PTR initialized instance with useful methods and properties:

// init ptr manually
var ptr = app.ptr.create('.ptr-content');

// or using get to retrieve already created instance
var ptr = app.ptr.get('.ptr-content');
Properties
ptr.appLink to global app instance
ptr.elPTR HTML element (ptr-content)
ptr.$elDom7 instance with PTR HTML element (ptr-content)
Methods
ptr.done()Reset PTR state
ptr.refresh()Trigger PTR
ptr.destroy()Destroy PTR instance and remove PTR event listeners from the specified HTML element

Pull To Refresh Events

PTR will fire the following DOM events on popup element and events on app and popup instance:

DOM Events

EventTargetDescription
ptr:pullstartPull To Refresh content<div class="ptr-content">Event will be triggered when you start to move pull to refresh content
ptr:pullmovePull To Refresh content<div class="ptr-content">Event will be triggered during you move pull to refresh content
ptr:pullendPull To Refresh content<div class="ptr-content">Event will be triggered when you release pull to refresh content
ptr:refreshPull To Refresh content<div class="ptr-content">Event will be triggered when pull to refresh enters in "refreshing" state. event.detail contain ptr.done method to reset its state after loading completed
ptr:donePull To Refresh content<div class="ptr-content">Event will be triggered after pull to refresh is done and it is back to initial state (after calling ptr.done method)
ptr:beforedestroyPull To Refresh content<div class="ptr-content">Event will be triggered right before PTR instance will be destroyed

App and Pull To Refresh Instance Events

PTR instance emits events on both self instance and app instance. App instance events has same names prefixed with ptr.

EventTargetArgumentsDescription
pullStartptr(el)Event will be triggered when you start to move pull to refresh content. As an argument event handler receives ptr element
ptrPullStartapp(el)
pullMoveptr(el, data)Event will be triggered during you move pull to refresh content. As an argument event handler receives ptr element and ptr data containing the following properties:
  • event - touchmove event
  • scrollTop - current scroll top position
  • translate - current translateY offset
  • touchesDiff - touches difference (in px)
ptrPullMoveapp(el, data)
pullEndptr(el, data)Event will be triggered when you release pull to refresh content. As an argument event handler receives ptr element
ptrPullEndapp(el, data)
refreshptr(el, done)Event will be triggered when pull to refresh enters in "refreshing" state. As an argument event handler receives ptr element and done function to reset PTR state
ptrRefreshapp(el, done)
doneptr(el)Event will be triggered after pull to refresh is done and it is back to initial state (after calling ptr.done method). As an argument event handler receives ptr element
ptrDoneapp(el)
beforeDestroyptr(ptr)Event will be triggered right before PTR instance will be destroyed. As an argument event handler receives PTR instance
ptrBeforeDestroyapp(ptr)

CSS Variables

Below is the list of related CSS variables (CSS custom properties).

.ios {
  --f7-ptr-preloader-size: 28px;
  --f7-ptr-size: 44px;
}
.md {
  --f7-ptr-preloader-size: 22px;
  --f7-ptr-size: 40px;
}
.aurora {
  --f7-ptr-preloader-size: 20px;
  --f7-ptr-size: 38px;
}

Examples

Pull From Top

<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="title">Pull To Refresh</div>
      </div>
    </div>
    <div class="page-content ptr-content" @ptr:refresh=${loadMore}>
      <div class="ptr-preloader">
        <div class="preloader"></div>
        <div class="ptr-arrow"></div>
      </div>
      <div class="list media-list">
        <ul>
          ${items.map((item) => $h`
          <li class="item-content">
            <div class="item-media"><img src=${item.picURL} width="44" /></div>
            <div class="item-inner">
              <div class="item-title-row">
                <div class="item-title">${item.song}</div>
              </div>
              <div class="item-subtitle">${item.author}</div>
            </div>
          </li>
          `)}
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
  export default (props, { $update }) => {
    // Dummy Content
    const songs = ['Yellow Submarine', 'Don\'t Stop Me Now', 'Billie Jean', 'Californication'];
    const authors = ['Beatles', 'Queen', 'Michael Jackson', 'Red Hot Chili Peppers'];
    let items = [
      {
        picURL: 'https://cdn.framework7.io/placeholder/abstract-88x88-1.jpg',
        song: 'Yellow Submarine',
        author: 'Beatles',
      },
      {
        picURL: 'https://cdn.framework7.io/placeholder/abstract-88x88-2.jpg',
        song: 'Don\'t Stop Me Now',
        author: 'Queen',
      },
      {
        picURL: 'https://cdn.framework7.io/placeholder/abstract-88x88-3.jpg',
        song: 'Billie Jean',
        author: 'Michael Jackson',
      },
    ]

    const loadMore = (e, done) => {
      // Emulate 2s loading
      setTimeout(() => {
        const picURL = 'https://cdn.framework7.io/placeholder/abstract-88x88-' + (Math.floor(Math.random() * 10) + 1) + '.jpg';
        const song = songs[Math.floor(Math.random() * songs.length)];
        const author = authors[Math.floor(Math.random() * authors.length)];
        // Add new item
        items.push({
          picURL,
          song,
          author,
        });

        // Update state to rerender
        $update();

        // When loading done, we need to reset it
        done();
      }, 2000);
    }

    return $render;
  }
</script>

Pull From Bottom

<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="title">Pull To Refresh Bottom</div>
      </div>
    </div>
    <div class="page-content ptr-content ptr-bottom" @ptr:refresh=${loadMore}>
      <div class="ptr-preloader">
        <div class="preloader"></div>
        <div class="ptr-arrow"></div>
      </div>
      <div class="list media-list">
        <ul>
          ${items.map((item) => $h`
          <li class="item-content">
            <div class="item-media"><img src=${item.picURL} width="44" /></div>
            <div class="item-inner">
              <div class="item-title-row">
                <div class="item-title">${item.song}</div>
              </div>
              <div class="item-subtitle">${item.author}</div>
            </div>
          </li>
          `)}
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
  export default (props, { $update }) => {
    // Dummy Content
    const songs = ['Yellow Submarine', 'Don\'t Stop Me Now', 'Billie Jean', 'Californication'];
    const authors = ['Beatles', 'Queen', 'Michael Jackson', 'Red Hot Chili Peppers'];
    let items = [
      {
        picURL: 'https://cdn.framework7.io/placeholder/abstract-88x88-1.jpg',
        song: 'Yellow Submarine',
        author: 'Beatles',
      },
      {
        picURL: 'https://cdn.framework7.io/placeholder/abstract-88x88-2.jpg',
        song: 'Don\'t Stop Me Now',
        author: 'Queen',
      },
      {
        picURL: 'https://cdn.framework7.io/placeholder/abstract-88x88-3.jpg',
        song: 'Billie Jean',
        author: 'Michael Jackson',
      },
    ]

    const loadMore = (e, done) => {
      // Emulate 2s loading
      setTimeout(() => {
        const picURL = 'https://cdn.framework7.io/placeholder/abstract-88x88-' + (Math.floor(Math.random() * 10) + 1) + '.jpg';
        const song = songs[Math.floor(Math.random() * songs.length)];
        const author = authors[Math.floor(Math.random() * authors.length)];
        // Add new item
        items.push({
          picURL,
          song,
          author,
        });

        // Update state to rerender
        $update();

        // When loading done, we need to reset it
        done();
      }, 2000);
    }

    return $render;
  }
</script>