├── img ├── arrow.png ├── spinner.gif └── iphone-frame.png ├── example ├── img │ └── iphone-frame.png ├── css │ ├── main.css │ └── normalize.min.css └── index.html ├── README.md ├── pull-to-refresh.css └── jquery.plugin.pullToRefresh.js /img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visiongeist/pull-to-refresh-js/HEAD/img/arrow.png -------------------------------------------------------------------------------- /img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visiongeist/pull-to-refresh-js/HEAD/img/spinner.gif -------------------------------------------------------------------------------- /img/iphone-frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visiongeist/pull-to-refresh-js/HEAD/img/iphone-frame.png -------------------------------------------------------------------------------- /example/img/iphone-frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/visiongeist/pull-to-refresh-js/HEAD/example/img/iphone-frame.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pull-to-refresh.js 2 | 3 | This plugin enables a pull-to-refresh functionality in mobile safari for scrollable block elements with native scrolling on iOS (!) 4 | 5 | Just create this markup 6 | 7 |
8 |
9 | 10 |
11 |
12 | 13 | and enable the plugin through passing a callback which returns a promise e.g. 14 | 15 | $('.scrollable').pullToRefresh({ 16 | callback: function() { 17 | var def = $.Deferred(); 18 | 19 | setTimeout(function() { 20 | def.resolve(); 21 | }, 3000); 22 | 23 | return def.promise(); 24 | } 25 | }); 26 | 27 | Don't forget to include jquery.plugin.pullToRefresh.js and pull-to-refresh.css 28 | 29 | Works for iOS5 and newer. 30 | 31 | ## Links 32 | 33 | * [How this works](http://damien.antipa.at/2012/10/16/ios-pull-to-refresh-in-mobile-safari-with-native-scrolling/) 34 | * [Demo](http://damien.antipa.at/demo/pull-to-refresh/example) 35 | * [My blog](http://damien.antipa.at) -------------------------------------------------------------------------------- /example/css/main.css: -------------------------------------------------------------------------------- 1 | header, footer { 2 | text-align: center; 3 | } 4 | 5 | .desktop header p { 6 | padding: 0.5em 3em; 7 | } 8 | 9 | .desktop #demo { 10 | -moz-box-sizing: border-box; 11 | -webkit-box-sizing: border-box; 12 | box-sizing: border-box; 13 | background: url('../img/iphone-frame.png') no-repeat; 14 | width: 227px; 15 | height: 443px; 16 | padding: 79px 19px; 17 | margin: 0 auto; 18 | } 19 | 20 | .touch { 21 | padding-bottom: 30px; 22 | } 23 | 24 | .touch #demo-window { 25 | border-top: 3px solid #0a0a0a; 26 | border-bottom: 3px solid #0a0a0a; 27 | } 28 | 29 | .touch ul.content > li.err { 30 | display: none; 31 | } 32 | 33 | .touch footer { 34 | position: fixed; 35 | bottom: 0; 36 | width: 100%; 37 | background: #fff; 38 | border-top: 1px solid #0a0a0a; 39 | } 40 | 41 | #demo-window { 42 | width: 100%; 43 | height: 287px; 44 | } 45 | 46 | ul.content { 47 | border-top: 1px solid #0a0a0a; 48 | list-style-type: none; 49 | padding: 0; 50 | margin: 0; 51 | } 52 | 53 | ul.content > li { 54 | border-bottom: 1px solid #0a0a0a; 55 | color: #0A0A0A; 56 | font-size: 80%; 57 | line-height: 30px; 58 | padding: 0 5px; 59 | } 60 | 61 | ul.content > li.err { 62 | color: red; 63 | font-weight: bold; 64 | } -------------------------------------------------------------------------------- /pull-to-refresh.css: -------------------------------------------------------------------------------- 1 | .scrollable { 2 | overflow-y: auto; 3 | height: 100%; 4 | -webkit-overflow-scrolling: touch; 5 | position: relative; 6 | } 7 | 8 | .scrollable .wrap { 9 | min-height: 100%; 10 | padding-bottom: 1px; 11 | background: #f5f5f5; 12 | -webkit-transform: translateZ(0); 13 | } 14 | 15 | .pull-to-refresh { 16 | line-height: 40px; 17 | height: 40px; 18 | width: 100%; 19 | 20 | position: absolute; 21 | left: 0; 22 | top: -40px; 23 | 24 | font-size: 0.85em; 25 | 26 | z-index: -1; 27 | } 28 | 29 | .pull-to-refresh >.message { 30 | height: 40px; 31 | width: 100%; 32 | overflow-y: auto; 33 | position: relative; 34 | } 35 | 36 | .pull-to-refresh >.message >i.arrow, 37 | .pull-to-refresh >.message >i.spinner { 38 | -webkit-background-size: 100% 100%; 39 | -moz-background-size: 100% 100%; 40 | background-size: 100% 100%; 41 | display: inline-block; 42 | margin-left: 10px; 43 | background-position: center center; 44 | 45 | } 46 | 47 | .pull-to-refresh >.message >i.arrow { 48 | width: 20px; 49 | height: 40px; 50 | background-image: url('img/arrow.png'); 51 | } 52 | 53 | .pull-to-refresh >.message >i.spinner { 54 | width: 16px; 55 | height: 16px; 56 | background-image: url('img/spinner.gif'); 57 | } 58 | 59 | .pull-to-refresh >.message >span.pull, 60 | .pull-to-refresh >.message >span.release, 61 | .pull-to-refresh >.message >span.loading { 62 | text-align: center; 63 | 64 | display: block; 65 | height: 40px; 66 | width: 100%; 67 | 68 | position: absolute; 69 | top: 0; 70 | left: 0; 71 | } -------------------------------------------------------------------------------- /example/css/normalize.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v1.0.1 | MIT License | git.io/normalize */ 2 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block} 3 | audio,canvas,video{display:inline-block;*display:inline;*zoom:1} 4 | audio:not([controls]){display:none;height:0} 5 | [hidden]{display:none} 6 | html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%} 7 | html,button,input,select,textarea{font-family:sans-serif} 8 | body{margin:0} 9 | a:focus{outline:thin dotted} 10 | a:active,a:hover{outline:0} 11 | h1{font-size:2em;margin:.67em 0} 12 | h2{font-size:1.5em;margin:.83em 0} 13 | h3{font-size:1.17em;margin:1em 0} 14 | h4{font-size:1em;margin:1.33em 0} 15 | h5{font-size:.83em;margin:1.67em 0} 16 | h6{font-size:.75em;margin:2.33em 0} 17 | abbr[title]{border-bottom:1px dotted} 18 | b,strong{font-weight:bold} 19 | blockquote{margin:1em 40px} 20 | dfn{font-style:italic} 21 | mark{background:#ff0;color:#000} 22 | p,pre{margin:1em 0} 23 | code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em} 24 | pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word} 25 | q{quotes:none} 26 | q:before,q:after{content:'';content:none} 27 | small{font-size:80%} 28 | sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} 29 | sup{top:-0.5em} 30 | sub{bottom:-0.25em} 31 | dl,menu,ol,ul{margin:1em 0} 32 | dd{margin:0 0 0 40px} 33 | menu,ol,ul{padding:0 0 0 40px} 34 | nav ul,nav ol{list-style:none;list-style-image:none} 35 | img{border:0;-ms-interpolation-mode:bicubic} 36 | svg:not(:root){overflow:hidden} 37 | figure{margin:0} 38 | form{margin:0} 39 | fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em} 40 | legend{border:0;padding:0;white-space:normal;*margin-left:-7px} 41 | button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle} 42 | button,input{line-height:normal} 43 | button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible} 44 | button[disabled],input[disabled]{cursor:default} 45 | input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px} 46 | input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box} 47 | input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none} 48 | button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} 49 | textarea{overflow:auto;vertical-align:top} 50 | table{border-collapse:collapse;border-spacing:0} -------------------------------------------------------------------------------- /jquery.plugin.pullToRefresh.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jquery.plugin.pullToRefresh.js 3 | * version 1.0 4 | * author: Damien Antipa 5 | * https://github.com/dantipa/pull-to-refresh-js 6 | */ 7 | (function( $ ){ 8 | 9 | $.fn.pullToRefresh = function( options ) { 10 | 11 | var isTouch = !!('ontouchstart' in window), 12 | cfg = $.extend(true, { 13 | message: { 14 | pull: 'Pull to refresh', 15 | release: 'Release to refresh', 16 | loading: 'Loading' 17 | } 18 | }, options), 19 | html = '
' + 20 | '
' + 21 | '
' + 22 | '' + 23 | '' + 24 | '' + cfg.message.pull + '' + 25 | '' + cfg.message.release + '' + 26 | '' + cfg.message.loading + '' + 27 | '
' + 28 | '
'; 29 | 30 | 31 | 32 | return this.each(function() { 33 | if (!isTouch) { 34 | return; 35 | } 36 | 37 | var e = $(this).prepend(html), 38 | content = e.find('.wrap'), 39 | ptr = e.find('.pull-to-refresh'), 40 | arrow = e.find('.arrow'), 41 | spinner = e.find('.spinner'), 42 | pull = e.find('.pull'), 43 | release = e.find('.release'), 44 | loading = e.find('.loading'), 45 | ptrHeight = ptr.height(), 46 | arrowDelay = ptrHeight / 3 * 2, 47 | isActivated = false, 48 | isLoading = false; 49 | 50 | content.on('touchstart', function (ev) { 51 | if (e.scrollTop() === 0) { // fix scrolling 52 | e.scrollTop(1); 53 | } 54 | }).on('touchmove', function (ev) { 55 | var top = e.scrollTop(), 56 | deg = 180 - (top < -ptrHeight ? 180 : // degrees to move for the arrow (starts at 180° and decreases) 57 | (top < -arrowDelay ? Math.round(180 / (ptrHeight - arrowDelay) * (-top - arrowDelay)) 58 | : 0)); 59 | 60 | if (isLoading) { // if is already loading -> do nothing 61 | return true; 62 | } 63 | 64 | arrow.show(); 65 | arrow.css('transform', 'rotate('+ deg + 'deg)'); // move arrow 66 | 67 | spinner.hide(); 68 | 69 | if (-top > ptrHeight) { // release state 70 | release.css('opacity', 1); 71 | pull.css('opacity', 0); 72 | loading.css('opacity', 0); 73 | 74 | 75 | isActivated = true; 76 | } else if (top > -ptrHeight) { // pull state 77 | release.css('opacity', 0); 78 | loading.css('opacity', 0); 79 | pull.css('opacity', 1); 80 | 81 | isActivated = false; 82 | } 83 | }).on('touchend', function(ev) { 84 | var top = e.scrollTop(); 85 | 86 | if (isActivated) { // loading state 87 | isLoading = true; 88 | isActivated = false; 89 | 90 | release.css('opacity', 0);; 91 | pull.css('opacity', 0); 92 | loading.css('opacity', 1); 93 | arrow.hide(); 94 | spinner.show(); 95 | 96 | ptr.css('position', 'static'); 97 | 98 | cfg.callback().done(function() { 99 | ptr.animate({ 100 | height: 10 101 | }, 'fast', 'linear', function () { 102 | ptr.css({ 103 | position: 'absolute', 104 | height: ptrHeight 105 | }); 106 | isLoading = false; 107 | }); 108 | }); 109 | } 110 | }); 111 | }); 112 | 113 | }; 114 | })( jQuery ); 115 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pull-to-refresh with native scrolling 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 |
19 |
20 |

Pull-to-refresh for native scrolling

21 |

22 | Since iOS 5 mobile Safari supports native scrolling for overflowing divs. Beforehand it was necessary to emulate the scrolling behavior with Javascript. The most popular scrolling libraries added some features to the scrolling part. Such as the pretty popular "Pull-to-refresh" functionality. 23 |

24 |

25 | This tiny jQuery plugin allows you to use "Pull-to-refresh" with native scrolling OOTB! Check out this blog entry to learn how it works or the github project to download. 26 |

27 |

28 | Cheers,
29 | Damien Antipa 30 |

31 |
32 |
33 |
34 |
35 |
36 |
    37 |
  • Works on
  • 38 |
  • touch devices only!
  • 39 |
  • Pull here to try
  • 40 |
  • You can't touch this
  • 41 |
  • My, my, my music
  • 42 |
  • hits me so hard
  • 43 |
  • Makes me say "Oh, my Lord"
  • 44 |
  • Thank you for blessing me
  • 45 |
  • I told you, homeboy
  • 46 |
  • You can't touch this
  • 47 |
  • Look at my eyes, man
  • 48 |
  • You can't touch this
  • 49 |
  • My, my, my music
  • 50 |
  • hits me so hard
  • 51 |
  • Makes me say "Oh, my Lord"
  • 52 |
  • Thank you for blessing me
  • 53 |
  • I told you, homeboy
  • 54 |
  • You can't touch this
  • 55 |
  • Look at my eyes, man
  • 56 |
  • You can't touch this
  • 57 |
  • My, my, my music
  • 58 |
  • hits me so hard
  • 59 |
  • Makes me say "Oh, my Lord"
  • 60 |
  • Thank you for blessing me
  • 61 |
  • I told you, homeboy
  • 62 |
  • You can't touch this
  • 63 |
  • Look at my eyes, man
  • 64 |
  • You can't touch this
  • 65 |
  • My, my, my music
  • 66 |
  • hits me so hard
  • 67 |
  • Makes me say "Oh, my Lord"
  • 68 |
  • Thank you for blessing me
  • 69 |
  • I told you, homeboy
  • 70 |
  • You can't touch this
  • 71 |
  • Look at my eyes, man
  • 72 |
  • You can't touch this
  • 73 |
  • My, my, my music
  • 74 |
  • hits me so hard
  • 75 |
  • Makes me say "Oh, my Lord"
  • 76 |
  • Thank you for blessing me
  • 77 |
  • I told you, homeboy
  • 78 |
  • You can't touch this
  • 79 |
  • Look at my eyes, man
  • 80 |
  • You can't touch this
  • 81 |
  • My, my, my music
  • 82 |
  • hits me so hard
  • 83 |
  • Makes me say "Oh, my Lord"
  • 84 |
  • Thank you for blessing me
  • 85 |
  • I told you, homeboy
  • 86 |
  • You can't touch this
  • 87 |
  • Look at my eyes, man
  • 88 |
  • You can't touch this
  • 89 |
  • My, my, my music
  • 90 |
  • hits me so hard
  • 91 |
  • Makes me say "Oh, my Lord"
  • 92 |
  • Thank you for blessing me
  • 93 |
  • I told you, homeboy
  • 94 |
  • You can't touch this
  • 95 |
  • Look at my eyes, man
  • 96 |
  • You can't touch this
  • 97 |
  • My, my, my music
  • 98 |
  • hits me so hard
  • 99 |
  • Makes me say "Oh, my Lord"
  • 100 |
  • Thank you for blessing me
  • 101 |
  • I told you, homeboy
  • 102 |
  • You can't touch this
  • 103 |
  • Look at my eyes, man
  • 104 |
  • You can't touch this
  • 105 |
  • My, my, my music
  • 106 |
  • hits me so hard
  • 107 |
  • Makes me say "Oh, my Lord"
  • 108 |
  • Thank you for blessing me
  • 109 |
  • I told you, homeboy
  • 110 |
  • You can't touch this
  • 111 |
  • Look at my eyes, man
  • 112 |
  • You can't touch this
  • 113 |
  • My, my, my music
  • 114 |
  • hits me so hard
  • 115 |
  • Makes me say "Oh, my Lord"
  • 116 |
  • Thank you for blessing me
  • 117 |
  • I told you, homeboy
  • 118 |
  • You can't touch this
  • 119 |
  • Look at my eyes, man
  • 120 |
  • You can't touch this
  • 121 |
  • My, my, my music
  • 122 |
  • hits me so hard
  • 123 |
  • Makes me say "Oh, my Lord"
  • 124 |
  • Thank you for blessing me
  • 125 |
  • I told you, homeboy
  • 126 |
  • You can't touch this
  • 127 |
  • Look at my eyes, man
  • 128 |
  • You can't touch this
  • 129 |
  • My, my, my music
  • 130 |
  • hits me so hard
  • 131 |
  • Makes me say "Oh, my Lord"
  • 132 |
  • Thank you for blessing me
  • 133 |
  • I told you, homeboy
  • 134 |
  • You can't touch this
  • 135 |
  • Look at my eyes, man
  • 136 |
  • You can't touch this
  • 137 |
  • My, my, my music
  • 138 |
  • hits me so hard
  • 139 |
  • Makes me say "Oh, my Lord"
  • 140 |
  • Thank you for blessing me
  • 141 |
  • I told you, homeboy
  • 142 |
  • You can't touch this
  • 143 |
  • Look at my eyes, man
  • 144 |
  • You can't touch this
  • 145 |
  • My, my, my music
  • 146 |
  • hits me so hard
  • 147 |
  • Makes me say "Oh, my Lord"
  • 148 |
  • Thank you for blessing me
  • 149 |
  • I told you, homeboy
  • 150 |
  • You can't touch this
  • 151 |
  • Look at my eyes, man
  • 152 |
  • You can't touch this
  • 153 |
  • My, my, my music
  • 154 |
  • hits me so hard
  • 155 |
  • Makes me say "Oh, my Lord"
  • 156 |
  • Thank you for blessing me
  • 157 |
  • I told you, homeboy
  • 158 |
  • You can't touch this
  • 159 |
  • Look at my eyes, man
  • 160 |
  • You can't touch this
  • 161 |
  • My, my, my music
  • 162 |
  • hits me so hard
  • 163 |
  • Makes me say "Oh, my Lord"
  • 164 |
  • Thank you for blessing me
  • 165 |
  • I told you, homeboy
  • 166 |
  • You can't touch this
  • 167 |
  • Look at my eyes, man
  • 168 |
  • You can't touch this
  • 169 |
  • My, my, my music
  • 170 |
  • hits me so hard
  • 171 |
  • Makes me say "Oh, my Lord"
  • 172 |
  • Thank you for blessing me
  • 173 |
  • I told you, homeboy
  • 174 |
  • You can't touch this
  • 175 |
  • Look at my eyes, man
  • 176 |
  • You can't touch this
  • 177 |
  • My, my, my music
  • 178 |
  • hits me so hard
  • 179 |
  • Makes me say "Oh, my Lord"
  • 180 |
  • Thank you for blessing me
  • 181 |
  • I told you, homeboy
  • 182 |
  • You can't touch this
  • 183 |
  • Look at my eyes, man
  • 184 |
  • You can't touch this
  • 185 |
  • My, my, my music
  • 186 |
  • hits me so hard
  • 187 |
  • Makes me say "Oh, my Lord"
  • 188 |
  • Thank you for blessing me
  • 189 |
  • I told you, homeboy
  • 190 |
  • You can't touch this
  • 191 |
  • Look at my eyes, man
  • 192 |
  • You can't touch this
  • 193 |
  • My, my, my music
  • 194 |
  • hits me so hard
  • 195 |
  • Makes me say "Oh, my Lord"
  • 196 |
  • Thank you for blessing me
  • 197 |
  • I told you, homeboy
  • 198 |
  • You can't touch this
  • 199 |
  • Look at my eyes, man
  • 200 |
  • You can't touch this
  • 201 |
  • My, my, my music
  • 202 |
  • hits me so hard
  • 203 |
  • Makes me say "Oh, my Lord"
  • 204 |
  • Thank you for blessing me
  • 205 |
  • I told you, homeboy
  • 206 |
  • You can't touch this
  • 207 |
  • Look at my eyes, man
  • 208 |
209 |
210 |
211 |
212 |
213 | 216 |
217 | 218 | 219 | 220 | 233 | 234 | 235 | --------------------------------------------------------------------------------