├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── build.js ├── demos ├── default.html ├── emoji.html ├── responsive.html └── touch.html ├── dist ├── basicSlider.min.css ├── basicSlider.min.js └── themes │ └── default.min.css ├── package.json └── src ├── scripts └── main.js └── styles ├── _slider.scss ├── _vars.scss ├── main.scss └── themes └── default.scss /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/**/* binary -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: electerious 2 | custom: ['https://paypal.me/electerious', 'https://www.buymeacoffee.com/electerious'] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | yarn.lock 3 | package-lock.json -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.1.5] - 2021-01-17 8 | 9 | ### Changed 10 | 11 | - Updated dependencies 12 | 13 | ## [1.1.4] - 2020-03-20 14 | 15 | ### Changed 16 | 17 | - Updated dependencies 18 | 19 | ## [1.1.3] - 2019-02-23 20 | 21 | ### Changed 22 | 23 | - Simplified demos 24 | - Replace `gulp` and `basicTasks` with custom build process 25 | 26 | ## [1.1.2] - 2017-12-17 27 | 28 | ### Changed 29 | 30 | - Syntax of JS and SCSS files 31 | 32 | ## [1.1.1] - 2017-08-08 33 | 34 | ### Added 35 | 36 | - Added a changelog 37 | 38 | ### Changed 39 | 40 | - Ignore `yarn.lock` and `package-lock.json` files -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Tobias Reich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # basicSlider 2 | 3 | [![Donate via PayPal](https://img.shields.io/badge/paypal-donate-009cde.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CYKBESW577YWE) 4 | 5 | A slider in its purest form. 6 | 7 | ## Contents 8 | 9 | - [Demos](#demos) 10 | - [Features](#features) 11 | - [Requirements](#requirements) 12 | - [Setup](#setup) 13 | - [API](#api) 14 | - [Instance API](#instance-api) 15 | - [Options](#options) 16 | - [Themes](#themes) 17 | 18 | ## Demos 19 | 20 | | Name | Description | Link | 21 | |:-----------|:------------|:------------| 22 | | Default | All features with the default theme. | [Try it on CodePen](http://codepen.io/electerious/pen/GjpXRX) | 23 | | Responsive | Responsive slider with dynamic content. | [Try it on CodePen](http://codepen.io/electerious/pen/aJKyxx) | 24 | | Emoji | Default theme with emojis. | [Try it on CodePen](http://codepen.io/electerious/pen/MpXEVd) | 25 | | Touch | Slider with swipe support. | [Try it on CodePen](https://codepen.io/electerious/pen/vWMXoL) | 26 | 27 | ## Features 28 | 29 | - Works in all modern browsers and IE11 ([with polyfills](#requirements)) 30 | - Supports any kind of content 31 | - No fancy shit, just a slider in its purest form 32 | - Zero dependencies 33 | - CommonJS and AMD support 34 | - Simple JS API 35 | 36 | ## Requirements 37 | 38 | basicSlider depends on the following browser APIs: 39 | 40 | - [Node​List​.prototype​.for​Each](https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach) 41 | - [Number.isFinite](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) 42 | - [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 43 | 44 | Some of these APIs are capable of being polyfilled in older browsers. Check the linked resources above to determine if you must polyfill to achieve your desired level of browser support. 45 | 46 | ## Setup 47 | 48 | We recommend installing basicSlider using [npm](https://npmjs.com) or [yarn](https://yarnpkg.com). 49 | 50 | ```sh 51 | npm install basicslider 52 | ``` 53 | 54 | ```sh 55 | yarn add basicslider 56 | ``` 57 | 58 | Include the CSS file in the `head` tag and the JS file at the end of your `body` tag… 59 | 60 | ```html 61 | 62 | 63 | ``` 64 | 65 | ```html 66 | 67 | ``` 68 | 69 | …or skip the JS file and use basicSlider as a module: 70 | 71 | ```js 72 | const basicSlider = require('basicslider') 73 | ``` 74 | 75 | ```js 76 | import * as basicSlider from 'basicslider' 77 | ``` 78 | 79 | ## API 80 | 81 | ### .create(elem, slides, opts) 82 | 83 | Creates a new basicSlider instance. 84 | 85 | Be sure to assign your instance to a variable. Using your instance, you can… 86 | 87 | * …get the current slide. 88 | * …navigate back and forward. 89 | * …goto a specific slide. 90 | 91 | Examples: 92 | 93 | ```js 94 | const instance = basicSlider.create(document.querySelector('#slider'), [ 95 | 'Slide 1', 96 | 'Slide 2', 97 | 'Slide 3' 98 | ]) 99 | ``` 100 | 101 | ```js 102 | const instance = basicSlider.create(document.querySelector('#slider'), [ 103 | '

Slide 1 with HTML

', 104 | '

Slide 2 with HTML

', 105 | '

Slide 3 with HTML

' 106 | ], { 107 | index: 1, 108 | arrows: false 109 | }) 110 | ``` 111 | 112 | Parameters: 113 | 114 | - `elem` `{Node}` The DOM element/node which should contain the slider. 115 | - `slides` `{Array}` Array of strings. Each item represents one slide. Any kind of HTML is allowed. 116 | - `opts` `{?Object}` An object of [options](#options). 117 | 118 | Returns: 119 | 120 | - `{Object}` The created instance. 121 | 122 | ## Instance API 123 | 124 | Each basicSlider instance has a handful of handy functions. Below are all of them along with a short description. 125 | 126 | ### .element() 127 | 128 | Returns the DOM element/node object associated with the instance. 129 | 130 | Example: 131 | 132 | ```js 133 | const elem = instance.element() 134 | ``` 135 | 136 | Returns: 137 | 138 | - `{Node}` DOM element/node associated with the instance. 139 | 140 | ### .length() 141 | 142 | Returns the total number of slides. 143 | 144 | Example: 145 | 146 | ```js 147 | const length = instance.length() 148 | ``` 149 | 150 | Returns: 151 | 152 | - `{Number}` Total number of slides. 153 | 154 | ### .current() 155 | 156 | Returns the current slide index. 157 | 158 | Example: 159 | 160 | ```js 161 | const current = instance.current() 162 | ``` 163 | 164 | Returns: 165 | 166 | - `{Number}` Current slide index. 167 | 168 | ### .goto(newIndex) 169 | 170 | Navigates to a slide by index and executes the beforeChange and afterChange callback functions. 171 | 172 | Example: 173 | 174 | ```js 175 | instance.goto(0) 176 | ``` 177 | 178 | Parameters: 179 | 180 | - `newIndex` `{Number}` Index of the slide to be displayed. 181 | 182 | ### .prev() 183 | 184 | Navigates to the previous slide and executes the beforeChange and afterChange callback functions. 185 | 186 | Example: 187 | 188 | ```js 189 | instance.prev() 190 | ``` 191 | 192 | ### .next() 193 | 194 | Navigates to the next slide and executes the beforeChange and afterChange callback functions. 195 | 196 | Example: 197 | 198 | ```js 199 | instance.next() 200 | ``` 201 | 202 | ## Options 203 | 204 | The option object can include the following properties: 205 | 206 | ```js 207 | { 208 | /* 209 | * Initial slide. 210 | */ 211 | index: 0, 212 | /* 213 | * Show or hide prev/next arrows. 214 | */ 215 | arrows: true, 216 | /* 217 | * Show or hide dot indicators. 218 | */ 219 | dots: true, 220 | /* 221 | * Callback functions. 222 | * Returning false will stop the caller function and prevent the slider from changing. 223 | */ 224 | beforeChange: (instance, newIndex, oldIndex) => {}, 225 | afterChange: (instance, newIndex, oldIndex) => {} 226 | } 227 | ``` 228 | 229 | ## Themes 230 | 231 | Layout and theme are separated CSS files. This makes it easy to style your own slider or to choose from the included themes. 232 | 233 | | Name | Demo | 234 | |:-----------|:------------| 235 | | Default theme | [Demo](http://codepen.io/electerious/pen/GjpXRX) | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basicSlider", 3 | "authors": [ 4 | "Tobias Reich " 5 | ], 6 | "description": "A slider in its purest form.", 7 | "main": [ 8 | "dist/basicSlider.min.css", 9 | "dist/basicSlider.min.js" 10 | ], 11 | "keywords": [ 12 | "slider", 13 | "slides", 14 | "carousel" 15 | ], 16 | "ignore": [ 17 | "**/.*", 18 | "demos", 19 | "build.js", 20 | "package.json" 21 | ], 22 | "license": "MIT", 23 | "homepage": "https://github.com/electerious/basicSlider", 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/electerious/basicSlider.git" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { writeFile } = require('fs').promises 4 | const js = require('rosid-handler-js') 5 | const sass = require('rosid-handler-sass') 6 | 7 | sass('src/styles/main.scss', { 8 | 9 | optimize: true 10 | 11 | }).then((data) => { 12 | 13 | return writeFile('dist/basicSlider.min.css', data) 14 | 15 | }) 16 | 17 | sass('src/styles/themes/default.scss', { 18 | 19 | optimize: true 20 | 21 | }).then((data) => { 22 | 23 | return writeFile('dist/themes/default.min.css', data) 24 | 25 | }) 26 | 27 | js('src/scripts/main.js', { 28 | 29 | optimize: true, 30 | babel: { 31 | presets: [ '@babel/preset-env' ], 32 | babelrc: false, 33 | global: true 34 | }, 35 | browserify: { 36 | standalone: 'basicSlider' 37 | } 38 | 39 | }).then((data) => { 40 | 41 | return writeFile('dist/basicSlider.min.js', data) 42 | 43 | }) -------------------------------------------------------------------------------- /demos/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | basicSlider Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 42 | 43 |
44 | 45 |
46 |
Default
47 |
1
48 |
2
49 |
3
50 |
51 | 52 |
53 |
Without arrows
54 |
1
55 |
2
56 |
3
57 |
58 | 59 |
60 |
Callbacks
61 |
1
62 |
2
63 |
3
64 |
65 | 66 |
67 |
Blocking Callback
68 |
1
69 |
2
70 |
3
71 |
72 | 73 |
74 |
0
75 |
Start with different index
76 |
2
77 |
3
78 |
79 | 80 |
81 |
Without dots
82 |
1
83 |
2
84 |
3
85 |
86 | 87 |
88 |
Without arrows and dots
89 |
1
90 |
2
91 |
3
92 |
93 | 94 |
95 | 96 | 97 | 98 | 156 | 157 | -------------------------------------------------------------------------------- /demos/emoji.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | basicSlider Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 95 | 96 |
97 | 98 |
99 |
Default
100 |
1
101 |
2
102 |
3
103 |
104 | 105 |
106 |
Default
107 |
1
108 |
2
109 |
3
110 |
111 | 112 |
113 |
Default
114 |
1
115 |
2
116 |
3
117 |
118 | 119 |
120 | 121 | 122 | 123 | 135 | 136 | -------------------------------------------------------------------------------- /demos/responsive.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | basicSlider Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 41 | 42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 65 |
66 | 67 | 68 | 69 | 137 | 138 | -------------------------------------------------------------------------------- /demos/touch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | basicSlider Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 42 | 43 |
44 | 45 |
46 |
Default
47 |
1
48 |
2
49 |
3
50 |
51 | 52 |
53 | 54 | 55 | 56 | 57 | 72 | 73 | -------------------------------------------------------------------------------- /dist/basicSlider.min.css: -------------------------------------------------------------------------------- 1 | .basicSlider{position:relative;width:100%}.basicSlider__arrow,.basicSlider__dot{cursor:pointer}.basicSlider__container{position:relative;width:100%;height:100%;overflow:hidden}.basicSlider__slides{position:relative;display:flex;height:100%;transition:transform .4s cubic-bezier(.51,.92,.24,1);will-change:transform}.basicSlider__slide{flex:0 1 auto;width:100%;height:100%} -------------------------------------------------------------------------------- /dist/basicSlider.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).basicSlider=e()}}((function(){return function e(n,t,r){function i(a,d){if(!t[a]){if(!n[a]){var c="function"==typeof require&&require;if(!d&&c)return c(a,!0);if(o)return o(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var u=t[a]={exports:{}};n[a][0].call(u.exports,(function(e){return i(n[a][1][e]||e)}),u,u.exports,e,n,t,r)}return t[a].exports}for(var o="function"==typeof require&&require,a=0;a=0&&(i=0+i),i<0&&(i=r+i),e+i}}},{}],2:[function(e,n,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.create=void 0;var r,i=(r=e("count-between"))&&r.__esModule?r:{default:r};var o="left",a=function(e){"function"==typeof e.stopPropagation&&e.stopPropagation(),"function"==typeof e.preventDefault&&e.preventDefault()},d=function(e,n){var t=document.createElement("button");return e=e===o?"left":"right",t.classList.add("basicSlider__arrow"),t.classList.add("basicSlider__arrow--".concat(e)),t.onclick=function(e){n(),a(e)},t},c=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",n=document.createElement("div");return n.classList.add("basicSlider__slide"),n.innerHTML=e,n},l=function(e,n,t,r){n.style.transform="translateX(-".concat(100/r*t,"%)"),e.forEach((function(e){return e.classList.remove("active")})),e[t].classList.add("active")},u=function(e,n,t,r){var u=(0,i.default)(0,t.length()-1,r.index),s={};return s.slideElems=n.map(c),s.dotElems=n.map((function(e,n){return function(e){var n=document.createElement("button");return n.classList.add("basicSlider__dot"),n.onclick=function(n){e(),a(n)},n}(t.goto.bind(null,n))})),s.dotsElem=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=document.createElement("div");return n.classList.add("basicSlider__dots"),e.forEach((function(e){return n.appendChild(e)})),n}(s.dotElems),s.slidesElem=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=document.createElement("div");return n.classList.add("basicSlider__slides"),n.style.width="".concat(100*e.length,"%"),e.forEach((function(e){return n.appendChild(e)})),n}(s.slideElems),s.containerElem=function(e){var n=document.createElement("div");return n.classList.add("basicSlider__container"),n.appendChild(e),n}(s.slidesElem),s.arrowLeftElem=d(o,t.prev),s.arrowRightElem=d("right",t.next),l(s.dotElems,s.slidesElem,u(),t.length()),function(e,n,t){var r=n.arrowLeftElem,i=n.arrowRightElem,o=n.dotsElem,a=n.containerElem;e.classList.add("basicSlider"),e.innerHTML="",e.appendChild(a),!0===t.arrows&&(e.appendChild(r),e.appendChild(i)),!0===t.dots&&e.appendChild(o)}(e,s,r),{c:u,refs:s}};t.create=function(e,n,t){t=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e=Object.assign({},e),!1===Number.isFinite(e.index)&&(e.index=0),!1!==e.arrows&&(e.arrows=!0),!1!==e.dots&&(e.dots=!0),"function"!=typeof e.beforeChange&&(e.beforeChange=function(){}),"function"!=typeof e.afterChange&&(e.afterChange=function(){}),e}(t);var r=null,o=null,a=function(){return n.length},d=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:r();if(!1===t.beforeChange(c,e,n))return!1;r=(0,i.default)(0,a()-1,e),l(o.dotElems,o.slidesElem,r(),a()),t.afterChange(c,e,n)},c={element:function(){return e},length:a,current:function(){return r()},goto:d,prev:function(){var e=r(),n=r(-1);d(n,e)},next:function(){var e=r(),n=r(1);d(n,e)}},s=u(e,n,c,t);return r=s.c,o=s.refs,c}},{"count-between":1}]},{},[2])(2)})); -------------------------------------------------------------------------------- /dist/themes/default.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";.basicSlider{margin:0 0 3rem}.basicSlider__arrow{position:absolute;top:calc(50% - 11px);padding:0;width:22px;height:22px;background:#333;color:#fff;text-align:center;border:none;border-radius:100%;opacity:.4;transition:opacity .4s cubic-bezier(.51,.92,.24,1)}.basicSlider__arrow:before{font-size:14px;line-height:22px}.basicSlider__arrow:hover{opacity:1}.basicSlider__arrow--left{left:6px}.basicSlider__arrow--left:before{content:"←"}.basicSlider__arrow--right{right:6px}.basicSlider__arrow--right:before{content:"→"}.basicSlider__dots{position:absolute;display:flex;justify-content:center;align-items:center;top:100%;width:100%;height:2rem}.basicSlider__dot{flex:0 0 auto;padding:6px;background:transparent;border:none;opacity:.4;transition:opacity .4s cubic-bezier(.51,.92,.24,1)}.basicSlider__dot:before{content:"";display:block;width:8px;height:8px;background:#333;border:none;border-radius:100%}.basicSlider__dot.active,.basicSlider__dot:hover{opacity:1} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basicslider", 3 | "version": "1.1.5", 4 | "authors": [ 5 | "Tobias Reich " 6 | ], 7 | "description": "A slider in its purest form.", 8 | "main": "dist/basicSlider.min.js", 9 | "keywords": [ 10 | "slider", 11 | "slides", 12 | "carousel" 13 | ], 14 | "scripts": { 15 | "build": "node build.js" 16 | }, 17 | "license": "MIT", 18 | "homepage": "https://github.com/electerious/basicSlider", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/electerious/basicSlider.git" 22 | }, 23 | "files": [ 24 | "dist", 25 | "src" 26 | ], 27 | "dependencies": { 28 | "count-between": "^3.0.0" 29 | }, 30 | "devDependencies": { 31 | "rosid-handler-js": "^13.0.2", 32 | "rosid-handler-sass": "^8.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/scripts/main.js: -------------------------------------------------------------------------------- 1 | import counter from 'count-between' 2 | 3 | const ARROW_LEFT = 'left' 4 | const ARROW_RIGHT = 'right' 5 | 6 | const stopEvent = function(e) { 7 | 8 | if (typeof e.stopPropagation === 'function') e.stopPropagation() 9 | if (typeof e.preventDefault === 'function') e.preventDefault() 10 | 11 | } 12 | 13 | const validate = function(opts = {}) { 14 | 15 | opts = Object.assign({}, opts) 16 | 17 | if (Number.isFinite(opts.index) === false) opts.index = 0 18 | 19 | if (opts.arrows !== false) opts.arrows = true 20 | if (opts.dots !== false) opts.dots = true 21 | 22 | if (typeof opts.beforeChange !== 'function') opts.beforeChange = () => {} 23 | if (typeof opts.afterChange !== 'function') opts.afterChange = () => {} 24 | 25 | return opts 26 | 27 | } 28 | 29 | const renderSlider = function(elem, refs, opts) { 30 | 31 | const { 32 | arrowLeftElem, 33 | arrowRightElem, 34 | dotsElem, 35 | containerElem 36 | } = refs 37 | 38 | // Add default class 39 | elem.classList.add('basicSlider') 40 | 41 | // Clear existing content 42 | elem.innerHTML = '' 43 | 44 | // Insert slider content 45 | elem.appendChild(containerElem) 46 | 47 | // Insert arrows at the end so they stay clickable 48 | if (opts.arrows === true) { 49 | elem.appendChild(arrowLeftElem) 50 | elem.appendChild(arrowRightElem) 51 | } 52 | 53 | // Insert dots at the end so they stay clickable 54 | if (opts.dots === true) { 55 | elem.appendChild(dotsElem) 56 | } 57 | 58 | } 59 | 60 | const renderArrow = function(direction, fn) { 61 | 62 | const elem = document.createElement('button') 63 | 64 | // Only allow left and right arrows 65 | direction = (direction === ARROW_LEFT ? 'left' : 'right') 66 | 67 | // Add the default and direction class 68 | elem.classList.add('basicSlider__arrow') 69 | elem.classList.add(`basicSlider__arrow--${ direction }`) 70 | 71 | // Bind click event 72 | elem.onclick = (e) => { 73 | fn() 74 | stopEvent(e) 75 | } 76 | 77 | return elem 78 | 79 | } 80 | 81 | const renderDots = function(dotElems = []) { 82 | 83 | const elem = document.createElement('div') 84 | 85 | // Add default class 86 | elem.classList.add('basicSlider__dots') 87 | 88 | // Add dots 89 | dotElems.forEach((dotElem) => elem.appendChild(dotElem)) 90 | 91 | return elem 92 | 93 | } 94 | 95 | const renderDot = function(fn) { 96 | 97 | const elem = document.createElement('button') 98 | 99 | // Add default class 100 | elem.classList.add('basicSlider__dot') 101 | 102 | // Bind click event 103 | elem.onclick = (e) => { 104 | fn() 105 | stopEvent(e) 106 | } 107 | 108 | return elem 109 | 110 | } 111 | 112 | const renderContainer = function(slidesElem) { 113 | 114 | const elem = document.createElement('div') 115 | 116 | // Add default class 117 | elem.classList.add('basicSlider__container') 118 | 119 | // Insert container content 120 | elem.appendChild(slidesElem) 121 | 122 | return elem 123 | 124 | } 125 | 126 | const renderSlides = function(slideElems = []) { 127 | 128 | const elem = document.createElement('div') 129 | 130 | // Add default class 131 | elem.classList.add('basicSlider__slides') 132 | 133 | // Set width to the number of slides 134 | // Each slide should have a width of 100% available 135 | elem.style.width = `${ slideElems.length * 100 }%` 136 | 137 | // Add slides 138 | slideElems.forEach((slideElem) => elem.appendChild(slideElem)) 139 | 140 | return elem 141 | 142 | } 143 | 144 | const renderSlide = function(html = '') { 145 | 146 | const elem = document.createElement('div') 147 | 148 | // Add default class 149 | elem.classList.add('basicSlider__slide') 150 | 151 | // Add slide content 152 | elem.innerHTML = html 153 | 154 | return elem 155 | 156 | } 157 | 158 | const setSlide = function(dotElems, slidesElem, index, length) { 159 | 160 | slidesElem.style.transform = `translateX(-${ (100 / length) * index }%)` 161 | 162 | dotElems.forEach((dotElem) => dotElem.classList.remove('active')) 163 | dotElems[index].classList.add('active') 164 | 165 | } 166 | 167 | const init = function(elem, slides, instance, opts) { 168 | 169 | // Initialize slide counter 170 | const c = counter(0, instance.length() - 1, opts.index) 171 | 172 | // Object containing references to all rendered elements 173 | const refs = {} 174 | 175 | // Render all elements 176 | refs.slideElems = slides.map(renderSlide) 177 | refs.dotElems = slides.map((_, i) => renderDot(instance.goto.bind(null, i))) 178 | refs.dotsElem = renderDots(refs.dotElems) 179 | refs.slidesElem = renderSlides(refs.slideElems) 180 | refs.containerElem = renderContainer(refs.slidesElem) 181 | refs.arrowLeftElem = renderArrow(ARROW_LEFT, instance.prev) 182 | refs.arrowRightElem = renderArrow(ARROW_RIGHT, instance.next) 183 | 184 | // Set initial slide 185 | setSlide(refs.dotElems, refs.slidesElem, c(), instance.length()) 186 | 187 | // Modify the target elem 188 | // Adds required classes and replaces its content 189 | renderSlider(elem, refs, opts) 190 | 191 | return { 192 | c, 193 | refs 194 | } 195 | 196 | } 197 | 198 | export const create = function(elem, slides, opts) { 199 | 200 | // Validate options 201 | opts = validate(opts) 202 | 203 | // Slide index counter 204 | let c = null 205 | 206 | // Object containing references to all rendered elements 207 | let refs = null 208 | 209 | // Returns the slider element 210 | const _element = () => elem 211 | 212 | // Return the total number of slides 213 | const _length = () => slides.length 214 | 215 | // Returns the current slide index 216 | const _current = () => c() 217 | 218 | // Navigate to a given slide 219 | // Use c() as the default oldIndex as the counter hasn't been recreated yet, 220 | // when called through the API. Internal functions can set a custom oldIndex. 221 | const _goto = (newIndex, oldIndex = c()) => { 222 | 223 | // Run beforePrev event 224 | // Stop execution when function returns false 225 | if (opts.beforeChange(instance, newIndex, oldIndex) === false) return false 226 | 227 | // Recreate counter with new initial value 228 | c = counter(0, _length() - 1, newIndex) 229 | 230 | // Switch to new slide 231 | setSlide(refs.dotElems, refs.slidesElem, c(), _length()) 232 | 233 | // Run afterShow event 234 | opts.afterChange(instance, newIndex, oldIndex) 235 | 236 | } 237 | 238 | // Navigate to the previous slide 239 | const _prev = () => { 240 | 241 | // Store old index before modifying the counter 242 | const oldIndex = c() 243 | const newIndex = c(-1) 244 | 245 | _goto(newIndex, oldIndex) 246 | 247 | } 248 | 249 | // Navigate to the next slide 250 | const _next = () => { 251 | 252 | // Store old index before modifying the counter 253 | const oldIndex = c() 254 | const newIndex = c(1) 255 | 256 | _goto(newIndex, oldIndex) 257 | 258 | } 259 | 260 | // Assign instance to a variable so the instance can be used 261 | // elsewhere in the current function. 262 | const instance = { 263 | element: _element, 264 | length: _length, 265 | current: _current, 266 | goto: _goto, 267 | prev: _prev, 268 | next: _next 269 | } 270 | 271 | // Initialize slider 272 | const initial = init(elem, slides, instance, opts) 273 | 274 | // Use returned values for initialization as those will be used 275 | // in functions of the instance 276 | c = initial.c 277 | refs = initial.refs 278 | 279 | return instance 280 | 281 | } -------------------------------------------------------------------------------- /src/styles/_slider.scss: -------------------------------------------------------------------------------- 1 | .basicSlider { 2 | 3 | position: relative; 4 | width: 100%; 5 | 6 | &__arrow { 7 | cursor: pointer; 8 | } 9 | 10 | &__dot { 11 | cursor: pointer; 12 | } 13 | 14 | &__container { 15 | position: relative; 16 | width: 100%; 17 | height: 100%; 18 | overflow: hidden; 19 | } 20 | 21 | &__slides { 22 | position: relative; 23 | display: flex; 24 | height: 100%; 25 | transition: transform $basicSlider__duration $basicSlider__timing; 26 | will-change: transform; 27 | } 28 | 29 | &__slide { 30 | flex: 0 1 auto; 31 | width: 100%; 32 | height: 100%; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/styles/_vars.scss: -------------------------------------------------------------------------------- 1 | $basicSlider__color: #333 !default; 2 | $basicSlider__duration: .4s !default; 3 | $basicSlider__timing: cubic-bezier(.51, .92, .24, 1) !default; -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | // Vars ---------------------------------------------------------------- // 2 | @import 'vars'; 3 | 4 | // Files --------------------------------------------------------------- // 5 | @import 'slider'; -------------------------------------------------------------------------------- /src/styles/themes/default.scss: -------------------------------------------------------------------------------- 1 | // Vars ---------------------------------------------------------------- // 2 | @import '../vars'; 3 | 4 | // Styles -------------------------------------------------------------- // 5 | .basicSlider { 6 | 7 | margin: 0 0 3rem; 8 | 9 | &__arrow { 10 | $size: 22px; 11 | 12 | position: absolute; 13 | top: calc(50% - #{ $size/2 }); 14 | padding: 0; 15 | width: $size; 16 | height: $size; 17 | background: $basicSlider__color; 18 | color: white; 19 | text-align: center; 20 | border: none; 21 | border-radius: 100%; 22 | opacity: .4; 23 | transition: opacity $basicSlider__duration $basicSlider__timing; 24 | 25 | &::before { 26 | font-size: 14px; 27 | line-height: $size; 28 | } 29 | 30 | &:hover { 31 | opacity: 1; 32 | } 33 | } 34 | 35 | &__arrow--left { 36 | left: 6px; 37 | 38 | &::before { 39 | content: '←'; 40 | } 41 | } 42 | 43 | &__arrow--right { 44 | right: 6px; 45 | 46 | &::before { 47 | content: '→'; 48 | } 49 | } 50 | 51 | &__dots { 52 | position: absolute; 53 | display: flex; 54 | justify-content: center; 55 | align-items: center; 56 | top: 100%; 57 | width: 100%; 58 | height: 2rem; 59 | } 60 | 61 | &__dot { 62 | flex: 0 0 auto; 63 | padding: 6px; 64 | background: transparent; 65 | border: none; 66 | opacity: .4; 67 | transition: opacity $basicSlider__duration $basicSlider__timing; 68 | 69 | &::before { 70 | $size: 8px; 71 | 72 | content: ''; 73 | display: block; 74 | width: $size; 75 | height: $size; 76 | background: $basicSlider__color; 77 | border: none; 78 | border-radius: 100%; 79 | } 80 | 81 | &:hover, 82 | &.active { 83 | opacity: 1; 84 | } 85 | } 86 | 87 | } --------------------------------------------------------------------------------