├── .eslintrc.js ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── book.json ├── dist ├── ajaxinate-min.min.js ├── ajaxinate.js └── ajaxinate.min.js ├── docs ├── .gitignore ├── _config.yml ├── book │ ├── README.md │ ├── SUMMARY.md │ ├── getting-started.md │ ├── license.md │ └── methods.md └── index.html ├── gulpfile.js ├── package-lock.json ├── package.json ├── src └── ajaxinate.js └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb-base" 3 | }; 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: cam 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env.json 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.0.11](https://github.com/Elkfox/Ajaxinate/compare/2.0.10...2.0.11) (2018-12-13) 2 | 3 | 4 | 5 | 6 | ## [2.0.10](https://github.com/Elkfox/Ajaxinate/compare/2.0.9...2.0.10) (2018-12-13) 7 | 8 | 9 | 10 | 11 | ## [2.0.9](https://github.com/Elkfox/Ajaxinate/compare/2.0.8...2.0.9) (2018-12-13) 12 | 13 | 14 | 15 | 16 | ## [2.0.8](https://github.com/Elkfox/Ajaxinate/compare/2.0.7...2.0.8) (2018-12-13) 17 | 18 | 19 | 20 | 21 | ## [2.0.7](https://github.com/Elkfox/Ajaxinate/compare/2.0.5...2.0.7) (2018-12-13) 22 | 23 | 24 | ### :memo: 25 | 26 | * Add more development notes ([814f9a0](https://github.com/Elkfox/Ajaxinate/commit/814f9a0)) 27 | * Add more development notes ([b1bd46e](https://github.com/Elkfox/Ajaxinate/commit/b1bd46e)) 28 | * Methods documentation and fix typos ([9a5baf4](https://github.com/Elkfox/Ajaxinate/commit/9a5baf4)) 29 | 30 | ### :package: 31 | 32 | * Package lock ([0138778](https://github.com/Elkfox/Ajaxinate/commit/0138778)) 33 | 34 | ### :sparkles: 35 | 36 | * Use innerHTML for the loading button ([5844524](https://github.com/Elkfox/Ajaxinate/commit/5844524)) 37 | 38 | ### :tropical_drink: 39 | 40 | * Add git moji to gulp version commit messages ([1fa1f66](https://github.com/Elkfox/Ajaxinate/commit/1fa1f66)) 41 | 42 | 43 | 44 | ## [2.0.6](https://github.com/Elkfox/Ajaxinate/compare/2.0.5...2.0.6) (2018-08-11) 45 | 46 | 47 | ### :memo: 48 | 49 | * Add more development notes ([814f9a0](https://github.com/Elkfox/Ajaxinate/commit/814f9a0)) 50 | * Add more development notes ([b1bd46e](https://github.com/Elkfox/Ajaxinate/commit/b1bd46e)) 51 | * Methods documentation and fix typos ([9a5baf4](https://github.com/Elkfox/Ajaxinate/commit/9a5baf4)) 52 | 53 | ### :package: 54 | 55 | * Package lock ([0138778](https://github.com/Elkfox/Ajaxinate/commit/0138778)) 56 | 57 | ### :sparkles: 58 | 59 | * Use innerHTML for the loading button ([5844524](https://github.com/Elkfox/Ajaxinate/commit/5844524)) 60 | 61 | 62 | 63 | ## [2.0.5](https://github.com/Elkfox/Ajaxinate/compare/2.0.4...2.0.5) (2018-07-23) 64 | 65 | 66 | ### :memo: 67 | 68 | * Add comments to the src ([9411a90](https://github.com/Elkfox/Ajaxinate/commit/9411a90)) 69 | * Add development notes ([25eef53](https://github.com/Elkfox/Ajaxinate/commit/25eef53)) 70 | 71 | 72 | 73 | ## [2.0.4](https://github.com/Elkfox/Ajaxinate/compare/2.0.3...2.0.4) (2018-07-06) 74 | 75 | 76 | 77 | 78 | ## [2.0.3](https://github.com/Elkfox/Ajaxinate/compare/2.0.2...2.0.3) (2018-07-05) 79 | 80 | 81 | ### :white_check_mark: 82 | 83 | * Update release commit messages. ([506bdcf](https://github.com/Elkfox/Ajaxinate/commit/506bdcf)) 84 | 85 | 86 | 87 | ## 2.0.2 (2018-07-05) 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /*============================================================================== 2 | ___ _ _ _ 3 | / || | | | | | 4 | \__ | | | | | | __ 5 | / |/ |/_) |/ / \_/\/ 6 | \___/|__/| \_/|__/\__/ /\_/ 7 | |\ 8 | |/ 9 | Ajaxinate 10 | version v2.0.11 11 | https://github.com/Elkfox/Ajaxinate 12 | Copyright (c) 2017 Elkfox Co Pty Ltd 13 | https://elkfox.com 14 | Project lead: George Butter 15 | MIT License 16 | ==============================================================================*/ 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all 26 | copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://travis-ci.org/Elkfox/Ajaxinate) 2 | 3 | # Ajaxinate 4 | 5 | Ajax pagination plugin for Shopify themes 6 | 7 | View the documentation and demo site to get started 8 | 9 | ### License 10 | 11 | The code is available under an MIT License. All copyright notices must remain untouched. 12 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "./docs/book" 3 | } 4 | -------------------------------------------------------------------------------- /dist/ajaxinate-min.min.js: -------------------------------------------------------------------------------- 1 | "use strict";function Ajaxinate(e){var t=e||{};this.settings=Object.assign({method:"scroll",container:"#AjaxinateContainer",pagination:"#AjaxinatePagination",offset:0,loadingText:"Loading",callback:null},t),this.addScrollListeners=this.addScrollListeners.bind(this),this.addClickListener=this.addClickListener.bind(this),this.checkIfPaginationInView=this.checkIfPaginationInView.bind(this),this.preventMultipleClicks=this.preventMultipleClicks.bind(this),this.removeClickListener=this.removeClickListener.bind(this),this.removeScrollListener=this.removeScrollListener.bind(this),this.removePaginationElement=this.removePaginationElement.bind(this),this.destroy=this.destroy.bind(this),this.containerElement=document.querySelector(this.settings.container),this.paginationElement=document.querySelector(this.settings.pagination),this.initialize()}Object.defineProperty(exports,"__esModule",{value:!0}),exports.Ajaxinate=Ajaxinate,Ajaxinate.prototype.initialize=function(){this.containerElement&&{click:this.addClickListener,scroll:this.addScrollListeners}[this.settings.method]()},Ajaxinate.prototype.addScrollListeners=function(){this.paginationElement&&(document.addEventListener("scroll",this.checkIfPaginationInView),window.addEventListener("resize",this.checkIfPaginationInView),window.addEventListener("orientationchange",this.checkIfPaginationInView))},Ajaxinate.prototype.addClickListener=function(){this.paginationElement&&(this.nextPageLinkElement=this.paginationElement.querySelector("a"),this.clickActive=!0,void 0!==this.nextPageLinkElement&&null!==this.nextPageLinkElement&&this.nextPageLinkElement.addEventListener("click",this.preventMultipleClicks))},Ajaxinate.prototype.preventMultipleClicks=function(e){e.preventDefault(),this.clickActive&&(this.nextPageLinkElement.innerText=this.settings.loadingText,this.nextPageUrl=this.nextPageLinkElement.href,this.clickActive=!1,this.loadMore())},Ajaxinate.prototype.checkIfPaginationInView=function(){var e=this.paginationElement.getBoundingClientRect().top-this.settings.offset,t=this.paginationElement.getBoundingClientRect().bottom+this.settings.offset;e<=window.innerHeight&&t>=0&&(this.nextPageLinkElement=this.paginationElement.querySelector("a"),this.removeScrollListener(),this.nextPageLinkElement&&(this.nextPageLinkElement.innerText=this.settings.loadingText,this.nextPageUrl=this.nextPageLinkElement.href,this.loadMore()))},Ajaxinate.prototype.loadMore=function(){this.request=new XMLHttpRequest,this.request.onreadystatechange=function(){if(this.request.responseXML&&4!==!this.request.readyState&&200!==!this.request.status){var e=this.request.responseXML.querySelectorAll(this.settings.container)[0],t=this.request.responseXML.querySelectorAll(this.settings.pagination)[0];this.containerElement.insertAdjacentHTML("beforeend",e.innerHTML),void 0===t?this.removePaginationElement():(this.paginationElement.innerHTML=t.innerHTML,this.settings.callback&&"function"==typeof this.settings.callback&&this.settings.callback(this.request.responseXML),this.initialize())}}.bind(this),this.request.open("GET",this.nextPageUrl),this.request.responseType="document",this.request.send()},Ajaxinate.prototype.removeClickListener=function(){this.nextPageLinkElement.removeEventListener("click",this.preventMultipleClicks)},Ajaxinate.prototype.removePaginationElement=function(){this.paginationElement.innerHTML="",this.destroy()},Ajaxinate.prototype.removeScrollListener=function(){document.removeEventListener("scroll",this.checkIfPaginationInView),window.removeEventListener("resize",this.checkIfPaginationInView),window.removeEventListener("orientationchange",this.checkIfPaginationInView)},Ajaxinate.prototype.destroy=function(){return{click:this.removeClickListener,scroll:this.removeScrollListener}[this.settings.method](),this},exports.default=Ajaxinate; -------------------------------------------------------------------------------- /dist/ajaxinate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Ajaxinate = Ajaxinate; 7 | /* @preserve 8 | * https://github.com/Elkfox/Ajaxinate 9 | * Copyright (c) 2017 Elkfox Co Pty Ltd (elkfox.com) 10 | * MIT License (do not remove above copyright!) 11 | */ 12 | 13 | /* Configurable options; 14 | * 15 | * method: scroll or click 16 | * container: selector of repeating content 17 | * pagination: selector of pagination container 18 | * offset: number of pixels before the bottom to start loading more on scroll 19 | * loadingText: 'Loading', The text shown during when appending new content 20 | * callback: null, callback function after new content is appended 21 | * 22 | * Usage; 23 | * 24 | * import {Ajaxinate} from 'ajaxinate'; 25 | * 26 | * new Ajaxinate({ 27 | * offset: 5000, 28 | * loadingText: 'Loading more...', 29 | * }); 30 | */ 31 | 32 | /* eslint-env browser */ 33 | function Ajaxinate(config) { 34 | var settings = config || {}; 35 | 36 | var defaults = { 37 | method: 'scroll', 38 | container: '#AjaxinateContainer', 39 | pagination: '#AjaxinatePagination', 40 | offset: 0, 41 | loadingText: 'Loading', 42 | callback: null 43 | }; 44 | 45 | // Merge custom configs with defaults 46 | this.settings = Object.assign(defaults, settings); 47 | 48 | // Functions 49 | this.addScrollListeners = this.addScrollListeners.bind(this); 50 | this.addClickListener = this.addClickListener.bind(this); 51 | this.checkIfPaginationInView = this.checkIfPaginationInView.bind(this); 52 | this.preventMultipleClicks = this.preventMultipleClicks.bind(this); 53 | this.removeClickListener = this.removeClickListener.bind(this); 54 | this.removeScrollListener = this.removeScrollListener.bind(this); 55 | this.removePaginationElement = this.removePaginationElement.bind(this); 56 | this.destroy = this.destroy.bind(this); 57 | 58 | // Selectors 59 | this.containerElement = document.querySelector(this.settings.container); 60 | this.paginationElement = document.querySelector(this.settings.pagination); 61 | this.initialize(); 62 | } 63 | 64 | Ajaxinate.prototype.initialize = function initialize() { 65 | if (!this.containerElement) { 66 | return; 67 | } 68 | 69 | var initializers = { 70 | click: this.addClickListener, 71 | scroll: this.addScrollListeners 72 | }; 73 | 74 | initializers[this.settings.method](); 75 | }; 76 | 77 | Ajaxinate.prototype.addScrollListeners = function addScrollListeners() { 78 | if (!this.paginationElement) { 79 | return; 80 | } 81 | 82 | document.addEventListener('scroll', this.checkIfPaginationInView); 83 | window.addEventListener('resize', this.checkIfPaginationInView); 84 | window.addEventListener('orientationchange', this.checkIfPaginationInView); 85 | }; 86 | 87 | Ajaxinate.prototype.addClickListener = function addClickListener() { 88 | if (!this.paginationElement) { 89 | return; 90 | } 91 | 92 | this.nextPageLinkElement = this.paginationElement.querySelector('a'); 93 | this.clickActive = true; 94 | 95 | if (typeof this.nextPageLinkElement !== 'undefined' && this.nextPageLinkElement !== null) { 96 | this.nextPageLinkElement.addEventListener('click', this.preventMultipleClicks); 97 | } 98 | }; 99 | 100 | Ajaxinate.prototype.preventMultipleClicks = function preventMultipleClicks(event) { 101 | event.preventDefault(); 102 | 103 | if (!this.clickActive) { 104 | return; 105 | } 106 | 107 | this.nextPageLinkElement.innerText = this.settings.loadingText; 108 | this.nextPageUrl = this.nextPageLinkElement.href; 109 | this.clickActive = false; 110 | 111 | this.loadMore(); 112 | }; 113 | 114 | Ajaxinate.prototype.checkIfPaginationInView = function checkIfPaginationInView() { 115 | var top = this.paginationElement.getBoundingClientRect().top - this.settings.offset; 116 | var bottom = this.paginationElement.getBoundingClientRect().bottom + this.settings.offset; 117 | 118 | if (top <= window.innerHeight && bottom >= 0) { 119 | this.nextPageLinkElement = this.paginationElement.querySelector('a'); 120 | this.removeScrollListener(); 121 | 122 | if (this.nextPageLinkElement) { 123 | this.nextPageLinkElement.innerText = this.settings.loadingText; 124 | this.nextPageUrl = this.nextPageLinkElement.href; 125 | 126 | this.loadMore(); 127 | } 128 | } 129 | }; 130 | 131 | Ajaxinate.prototype.loadMore = function loadMore() { 132 | this.request = new XMLHttpRequest(); 133 | 134 | this.request.onreadystatechange = function success() { 135 | if (!this.request.responseXML) { 136 | return; 137 | } 138 | if (!this.request.readyState === 4 || !this.request.status === 200) { 139 | return; 140 | } 141 | 142 | var newContainer = this.request.responseXML.querySelectorAll(this.settings.container)[0]; 143 | var newPagination = this.request.responseXML.querySelectorAll(this.settings.pagination)[0]; 144 | 145 | this.containerElement.insertAdjacentHTML('beforeend', newContainer.innerHTML); 146 | 147 | if (typeof newPagination === 'undefined') { 148 | this.removePaginationElement(); 149 | } else { 150 | this.paginationElement.innerHTML = newPagination.innerHTML; 151 | 152 | if (this.settings.callback && typeof this.settings.callback === 'function') { 153 | this.settings.callback(this.request.responseXML); 154 | } 155 | 156 | this.initialize(); 157 | } 158 | }.bind(this); 159 | 160 | this.request.open('GET', this.nextPageUrl); 161 | this.request.responseType = 'document'; 162 | this.request.send(); 163 | }; 164 | 165 | Ajaxinate.prototype.removeClickListener = function removeClickListener() { 166 | this.nextPageLinkElement.removeEventListener('click', this.preventMultipleClicks); 167 | }; 168 | 169 | Ajaxinate.prototype.removePaginationElement = function removePaginationElement() { 170 | this.paginationElement.innerHTML = ''; 171 | this.destroy(); 172 | }; 173 | 174 | Ajaxinate.prototype.removeScrollListener = function removeScrollListener() { 175 | document.removeEventListener('scroll', this.checkIfPaginationInView); 176 | window.removeEventListener('resize', this.checkIfPaginationInView); 177 | window.removeEventListener('orientationchange', this.checkIfPaginationInView); 178 | }; 179 | 180 | Ajaxinate.prototype.destroy = function destroy() { 181 | var destroyers = { 182 | click: this.removeClickListener, 183 | scroll: this.removeScrollListener 184 | }; 185 | 186 | destroyers[this.settings.method](); 187 | 188 | return this; 189 | }; 190 | 191 | exports.default = Ajaxinate; -------------------------------------------------------------------------------- /dist/ajaxinate.min.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Ajaxinate = Ajaxinate; 7 | /* @preserve 8 | * https://github.com/Elkfox/Ajaxinate 9 | * Copyright (c) 2017 Elkfox Co Pty Ltd (elkfox.com) 10 | * MIT License (do not remove above copyright!) 11 | */ 12 | 13 | /* Configurable options; 14 | * 15 | * method: scroll or click 16 | * container: selector of repeating content 17 | * pagination: selector of pagination container 18 | * offset: number of pixels before the bottom to start loading more on scroll 19 | * loadingText: 'Loading', The text shown during when appending new content 20 | * callback: null, callback function after new content is appended 21 | * 22 | * Usage; 23 | * 24 | * import {Ajaxinate} from 'ajaxinate'; 25 | * 26 | * new Ajaxinate({ 27 | * offset: 5000, 28 | * loadingText: 'Loading more...', 29 | * }); 30 | */ 31 | 32 | /* eslint-env browser */ 33 | function Ajaxinate(config) { 34 | var settings = config || {}; 35 | 36 | var defaults = { 37 | method: 'scroll', 38 | container: '#AjaxinateContainer', 39 | pagination: '#AjaxinatePagination', 40 | offset: 0, 41 | loadingText: 'Loading', 42 | callback: null 43 | }; 44 | 45 | // Merge custom configs with defaults 46 | this.settings = Object.assign(defaults, settings); 47 | 48 | // Functions 49 | this.addScrollListeners = this.addScrollListeners.bind(this); 50 | this.addClickListener = this.addClickListener.bind(this); 51 | this.checkIfPaginationInView = this.checkIfPaginationInView.bind(this); 52 | this.preventMultipleClicks = this.preventMultipleClicks.bind(this); 53 | this.removeClickListener = this.removeClickListener.bind(this); 54 | this.removeScrollListener = this.removeScrollListener.bind(this); 55 | this.removePaginationElement = this.removePaginationElement.bind(this); 56 | this.destroy = this.destroy.bind(this); 57 | 58 | // Selectors 59 | this.containerElement = document.querySelector(this.settings.container); 60 | this.paginationElement = document.querySelector(this.settings.pagination); 61 | this.initialize(); 62 | } 63 | 64 | Ajaxinate.prototype.initialize = function initialize() { 65 | if (!this.containerElement) { 66 | return; 67 | } 68 | 69 | var initializers = { 70 | click: this.addClickListener, 71 | scroll: this.addScrollListeners 72 | }; 73 | 74 | initializers[this.settings.method](); 75 | }; 76 | 77 | Ajaxinate.prototype.addScrollListeners = function addScrollListeners() { 78 | if (!this.paginationElement) { 79 | return; 80 | } 81 | 82 | document.addEventListener('scroll', this.checkIfPaginationInView); 83 | window.addEventListener('resize', this.checkIfPaginationInView); 84 | window.addEventListener('orientationchange', this.checkIfPaginationInView); 85 | }; 86 | 87 | Ajaxinate.prototype.addClickListener = function addClickListener() { 88 | if (!this.paginationElement) { 89 | return; 90 | } 91 | 92 | this.nextPageLinkElement = this.paginationElement.querySelector('a'); 93 | this.clickActive = true; 94 | 95 | if (typeof this.nextPageLinkElement !== 'undefined' && this.nextPageLinkElement !== null) { 96 | this.nextPageLinkElement.addEventListener('click', this.preventMultipleClicks); 97 | } 98 | }; 99 | 100 | Ajaxinate.prototype.preventMultipleClicks = function preventMultipleClicks(event) { 101 | event.preventDefault(); 102 | 103 | if (!this.clickActive) { 104 | return; 105 | } 106 | 107 | this.nextPageLinkElement.innerText = this.settings.loadingText; 108 | this.nextPageUrl = this.nextPageLinkElement.href; 109 | this.clickActive = false; 110 | 111 | this.loadMore(); 112 | }; 113 | 114 | Ajaxinate.prototype.checkIfPaginationInView = function checkIfPaginationInView() { 115 | var top = this.paginationElement.getBoundingClientRect().top - this.settings.offset; 116 | var bottom = this.paginationElement.getBoundingClientRect().bottom + this.settings.offset; 117 | 118 | if (top <= window.innerHeight && bottom >= 0) { 119 | this.nextPageLinkElement = this.paginationElement.querySelector('a'); 120 | this.removeScrollListener(); 121 | 122 | if (this.nextPageLinkElement) { 123 | this.nextPageLinkElement.innerText = this.settings.loadingText; 124 | this.nextPageUrl = this.nextPageLinkElement.href; 125 | 126 | this.loadMore(); 127 | } 128 | } 129 | }; 130 | 131 | Ajaxinate.prototype.loadMore = function loadMore() { 132 | this.request = new XMLHttpRequest(); 133 | 134 | this.request.onreadystatechange = function success() { 135 | if (!this.request.responseXML) { 136 | return; 137 | } 138 | if (!this.request.readyState === 4 || !this.request.status === 200) { 139 | return; 140 | } 141 | 142 | var newContainer = this.request.responseXML.querySelectorAll(this.settings.container)[0]; 143 | var newPagination = this.request.responseXML.querySelectorAll(this.settings.pagination)[0]; 144 | 145 | this.containerElement.insertAdjacentHTML('beforeend', newContainer.innerHTML); 146 | 147 | if (typeof newPagination === 'undefined') { 148 | this.removePaginationElement(); 149 | } else { 150 | this.paginationElement.innerHTML = newPagination.innerHTML; 151 | 152 | if (this.settings.callback && typeof this.settings.callback === 'function') { 153 | this.settings.callback(this.request.responseXML); 154 | } 155 | 156 | this.initialize(); 157 | } 158 | }.bind(this); 159 | 160 | this.request.open('GET', this.nextPageUrl); 161 | this.request.responseType = 'document'; 162 | this.request.send(); 163 | }; 164 | 165 | Ajaxinate.prototype.removeClickListener = function removeClickListener() { 166 | this.nextPageLinkElement.removeEventListener('click', this.preventMultipleClicks); 167 | }; 168 | 169 | Ajaxinate.prototype.removePaginationElement = function removePaginationElement() { 170 | this.paginationElement.innerHTML = ''; 171 | this.destroy(); 172 | }; 173 | 174 | Ajaxinate.prototype.removeScrollListener = function removeScrollListener() { 175 | document.removeEventListener('scroll', this.checkIfPaginationInView); 176 | window.removeEventListener('resize', this.checkIfPaginationInView); 177 | window.removeEventListener('orientationchange', this.checkIfPaginationInView); 178 | }; 179 | 180 | Ajaxinate.prototype.destroy = function destroy() { 181 | var destroyers = { 182 | click: this.removeClickListener, 183 | scroll: this.removeScrollListener 184 | }; 185 | 186 | destroyers[this.settings.method](); 187 | 188 | return this; 189 | }; 190 | 191 | exports.default = Ajaxinate; -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-metadata 4 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | exclude: 3 | - book/ 4 | 5 | # Meta ======================================================================= 6 | title: Ajaxinate - Shopify endless scroll pagination plugin 7 | description: Shopify ajax pagination plugin 8 | 9 | # Site ======================================================================= 10 | name: Ajaxinate 11 | subtitle: Endless scrolling made easy 12 | github_url: https://github.com/Elkfox/Ajaxinate 13 | download_url: https://github.com/Elkfox/Ajaxinate/archive/master.zip 14 | demo_url: https://ajaxify.myshopify.com/collections/all 15 | -------------------------------------------------------------------------------- /docs/book/README.md: -------------------------------------------------------------------------------- 1 | # Shopify endless scroll plugin 2 | 3 | ## Features 4 | 5 | * No dependencies 6 | * Multiple methods; 'Endless scroll' automatically adds content as you scroll, whereas 'endless click' adds content on click 7 | * Graceful fallback when JavaScript is not available 8 | * Created for Shopify, but will work on any site 9 | 10 | ## Demos 11 | 12 | * [**Endless scroll demo**](https://ajaxinate.myshopify.com/) ↗ 13 | * [**Endless click demo**](https://ajaxinate.myshopify.com/collections/all?view=endless-click) ↗ 14 | 15 | ## Getting started 16 | 17 | You can either use NPM to add the plugin to your project, or manually add and the configure the code as necessary 18 | 19 | {% page-ref page="getting-started.md" %} 20 | 21 | ## Settings 22 | 23 | If you would like to change the names of the selectors, you can pass them in with the following settings: 24 | 25 | | Option | Default | Type | Description | 26 | | :--- | :--- | :--- | :--- | 27 | | container | **\#AjaxinateContainer** | String | Selector to identify the container element you want to paginate | 28 | | pagination | **\#AjaxinatePagination** | String | Selector to identify the pagination element | 29 | | method | **scroll** | String | Changes the method to 'endless click when set to' \`click\` | 30 | | offset | **0** | Integer | The distance required to scroll before sending a request | 31 | | loadingText | **Loading** | String | The text of the pagination link during a request | 32 | | callback | null | Function | Function fired after the new page has been loaded | 33 | 34 | For example: 35 | 36 | ```javascript 37 | document.addEventListener("DOMContentLoaded", function() { 38 | var endlessScroll = new Ajaxinate({ 39 | container: '#AjaxinateContainer', 40 | pagination: '#AjaxinatePagination', 41 | method: 'click', 42 | offset: 1000 43 | }); 44 | }); 45 | ``` 46 | 47 | ## Methods 48 | 49 | {% page-ref page="methods.md" %} 50 | 51 | -------------------------------------------------------------------------------- /docs/book/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Shopify endless scroll plugin](README.md) 4 | * [Getting started](getting-started.md) 5 | * [Methods](methods.md) 6 | * [License](license.md) 7 | 8 | ## Links 9 | 10 | * [Github](https://github.com/Elkfox/Ajaxinate) 11 | 12 | -------------------------------------------------------------------------------- /docs/book/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | ## NPM 4 | 5 | ```bash 6 | $ npm i ajaxinate 7 | ``` 8 | 9 | ```javascript 10 | import {Ajaxinate} from 'ajaxinate'; 11 | 12 | new Ajaxinate({ 13 | container: '#AjaxinateContainer', 14 | pagination: '#AjaxinatePagination', 15 | loadingText: 'Loading more...', 16 | }); 17 | ``` 18 | 19 | ## Manual installation 20 | 21 | **IMPORTANT! If you are using Ajaxinate without a package manager, use** [**the v2 branch**](https://github.com/Elkfox/Ajaxinate/tree/v2) **↗** 22 | 23 | 1. Add ajaxinate.min.js to the assets folder of your shopify theme, or add it to your vendor files if you are using Slate or a similar method. 24 | 2. Add the ajaxinate.min.js script src tag before the closing body tag, or defer its loading: 25 | 26 | {% code title="collection.liquid" %} 27 | ```python 28 | {{ 'ajaxinate.min.js' | asset_url | script_tag }} 29 | ``` 30 | {% endcode %} 31 | 32 | 3. Setup your collection or blog template, for example: 33 | 34 | {% code title="collection.liquid" %} 35 | ```python 36 | {% paginate collection.products by 3 %} 37 |
Redirecting to https://ajaxinate.elkfox.io
31 | 32 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Default gulp modules 4 | const gulp = require('gulp'); 5 | const tap = require('gulp-tap'); 6 | const babel = require('gulp-babel'); 7 | const minify = require('gulp-minify'); 8 | const rename = require('gulp-rename'); // Helps rename files or changing extensions 9 | const babelMinify = require("gulp-babel-minify"); // Converts to ES5 before minifying 10 | const gutil = require('gulp-util'); // Helps with debugging 11 | const eslint = require('gulp-eslint'); 12 | 13 | // Additional release gulp modules 14 | const env = require('gulp-env'); // For accessing environment variables 15 | const runSequence = require('run-sequence'); // Runs a sequence of gulp tasks 16 | const conventionalChangelog = require('gulp-conventional-changelog'); // Generates a changelog from git metadata 17 | const args = require('yargs').argv; // Add additional arguments to the commands 18 | const conventionalGithubReleaser = require('conventional-github-releaser'); // Make a new release from github metadata 19 | const bump = require('gulp-bump'); // Increases the version number 20 | const git = require('gulp-git'); // Run git functions with gulp 21 | const fs = require('fs'); // For working with the local file system 22 | 23 | // Define the location of our build directory 24 | const destination = 'dist/'; 25 | const source = 'src/ajaxinate.js'; 26 | 27 | var type = 'patch'; 28 | var version = null; 29 | 30 | function getPackageJsonVersion () { 31 | // We parse the json file instead of using require because require caches 32 | // multiple calls so the version number won't be updated 33 | return JSON.parse(fs.readFileSync('./package.json', 'utf8')).version; 34 | }; 35 | 36 | // The default task, run it using `gulp` 37 | // Converts source to ES5 and creates a minified version and builds to dist 38 | gulp.task('build', function() { 39 | return gulp.src(source) 40 | // First convert it to ecma2015 41 | .pipe(babel({ 42 | presets: ['env'] 43 | })) 44 | // Add a non minified version to the dist 45 | .pipe(gulp.dest(destination)) 46 | .pipe(minify()) 47 | // If there is an error during minification this will pretty print to the console 48 | .on('error', function (err) { gutil.log(gutil.colors.red('[Error]'), err.toString()); }) 49 | // Then we can adjust the extension include min 50 | .pipe(rename({ extname: '.min.js' })) 51 | // Then we output to the destination 52 | .pipe(gulp.dest(destination)); 53 | }); 54 | 55 | // Add the new version to the changelog 56 | gulp.task('changelog', function () { 57 | return gulp.src('CHANGELOG.md', { 58 | buffer: false 59 | }) 60 | .pipe(conventionalChangelog({ 61 | preset: 'atom' 62 | })) 63 | .pipe(gulp.dest('./')); 64 | }); 65 | 66 | // Ensure you duplicated the .env-sample and set your own GitHub token and renamed it .env 67 | // Create a convention github release 68 | gulp.task('github-release', function(done) { 69 | env({file: ".env.json"}); 70 | gutil.log(gutil.colors.blue('[github]'), 'Pushing to github using authtoken: '+process.env.GITHUB_AUTH_KEY); 71 | conventionalGithubReleaser({ 72 | type: "oauth", 73 | url: "https://api.github.com/", 74 | token: process.env.GITHUB_AUTH_KEY 75 | }, { 76 | preset: 'atom' 77 | }, done); 78 | }); 79 | 80 | gulp.task('bump-version', function () { 81 | // We hardcode the version change type to 'patch' but it may be a good idea to 82 | // use minimist (https://www.npmjs.com/package/minimist) to determine with a 83 | // command argument whether you are doing a 'major', 'minor' or a 'patch' change. 84 | return gulp.src(['./bower.json', './package.json']) 85 | .pipe(bump({type: type}).on('error', gutil.log)) 86 | .pipe(gulp.dest('./')) 87 | .pipe(tap(function(file){ 88 | var json = JSON.parse(String(file.contents)); 89 | version = json.version; 90 | })); 91 | }); 92 | 93 | gulp.task('commit-changes', function () { 94 | return gulp.src('.') 95 | .pipe(git.add()) 96 | .pipe(git.commit(`:bookmark: version: ${version} [${type}] `)); 97 | }); 98 | 99 | gulp.task('push-changes', function (cb) { 100 | git.push('origin', 'master', cb); 101 | }); 102 | 103 | gulp.task('create-new-tag', function (cb) { 104 | git.tag(version, 'Created Tag for version: ' + version, function (error) { 105 | if (error) { 106 | return cb(error); 107 | } 108 | git.push('origin', 'master', {args: '--tags'}, cb); 109 | }); 110 | }); 111 | 112 | gulp.task('lint', function() { 113 | return gulp.src(source) 114 | .pipe(eslint({ 115 | envs: ['node', 'browser'] 116 | })) 117 | .pipe(eslint.format()) 118 | .pipe(eslint.failAfterError()); 119 | }); 120 | 121 | gulp.task('license', function() { 122 | gulp.src('./LICENSE') 123 | .pipe(bump()) 124 | .pipe(gulp.dest('./')); 125 | gulp.src('./src/ajaxinate.js') 126 | .pipe(bump()) 127 | .pipe(gulp.dest('./src/')); 128 | }); 129 | 130 | gulp.task('release', function (callback) { 131 | if ( 132 | args.type === 'minor' || 133 | args.type === 'major' || 134 | args.type === 'prerelease' 135 | ) { 136 | type = args.type; 137 | } 138 | runSequence( 139 | 'lint', 140 | 'bump-version', 141 | 'license', 142 | 'changelog', 143 | 'build', 144 | 'commit-changes', 145 | 'push-changes', 146 | 'create-new-tag', 147 | 'github-release', 148 | function (error) { 149 | if (error) { 150 | console.log(error.message); 151 | } else { 152 | console.log('RELEASE FINISHED SUCCESSFULLY'); 153 | } 154 | callback(error); 155 | }); 156 | }); 157 | 158 | gulp.task('testrelease', function (callback) { 159 | runSequence( 160 | 'lint', 161 | 'log' 162 | ) 163 | }); 164 | gulp.task('log', function (callback) { 165 | if( 166 | args.type === 'minor' || 167 | args.type === 'major' || 168 | args.type === 'prerelease' 169 | ) { 170 | type = args.type; 171 | } 172 | 173 | gutil.log(args.type); 174 | gutil.log(type); 175 | }); 176 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ajaxinate", 3 | "license": "MIT", 4 | "description": "Ajax Pagination Javascript Plugin", 5 | "version": "3.0.1", 6 | "author": "Elkfox