├── demo-assets ├── images │ ├── sepicol.jpg │ ├── dogs-after.jpg │ ├── dogs-before.jpg │ ├── man-hold-beer.jpg │ ├── original-baltic.jpg │ ├── warmsphere-baltic.jpg │ ├── peitlerkofel-mountains.jpg │ ├── man-hold-beer-after1logo.jpg │ ├── peitlerkofel-mountains-original.jpg │ └── logos │ │ ├── Before_After.svg │ │ └── logo_cup.svg ├── demo.css └── prism │ ├── prism.css │ └── prism.js ├── .gitignore ├── src ├── index.js ├── beerslider.js ├── style.scss └── index.html ├── .travis.yml ├── .editorconfig ├── tests ├── index.js └── src │ └── index.js ├── webpack.config.js ├── webpack.common.js ├── webpack.development.js ├── webpack.test.js ├── webpack.production.js ├── webpack.demo.js ├── .eslintrc.js ├── LICENSE ├── dist ├── BeerSlider.css ├── BeerSlider.unmin.css └── BeerSlider.js ├── package.json ├── demo ├── BeerSlider.css ├── index.html └── BeerSlider.js ├── CODE_OF_CONDUCT.md ├── README.md └── karma.conf.js /demo-assets/images/sepicol.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/sepicol.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | log/*.log 4 | tmp/** 5 | coverage/ 6 | package/ 7 | *.tgz 8 | *.zip 9 | -------------------------------------------------------------------------------- /demo-assets/images/dogs-after.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/dogs-after.jpg -------------------------------------------------------------------------------- /demo-assets/images/dogs-before.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/dogs-before.jpg -------------------------------------------------------------------------------- /demo-assets/images/man-hold-beer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/man-hold-beer.jpg -------------------------------------------------------------------------------- /demo-assets/images/original-baltic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/original-baltic.jpg -------------------------------------------------------------------------------- /demo-assets/images/warmsphere-baltic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/warmsphere-baltic.jpg -------------------------------------------------------------------------------- /demo-assets/images/peitlerkofel-mountains.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/peitlerkofel-mountains.jpg -------------------------------------------------------------------------------- /demo-assets/images/man-hold-beer-after1logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/man-hold-beer-after1logo.jpg -------------------------------------------------------------------------------- /demo-assets/images/peitlerkofel-mountains-original.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LUX/beerslider/master/demo-assets/images/peitlerkofel-mountains-original.jpg -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './style.scss' 2 | 3 | import Promise from 'core-js/features/promise' 4 | 5 | import { BeerSlider as defaultExport } from './beerslider' 6 | 7 | export default defaultExport 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | branches: 7 | only: 8 | - master 9 | node_js: 10 | - 7 11 | ~notifications: 12 | email: false 13 | script: 14 | - npm run validate 15 | after_success: 16 | - npm run report-coverage 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{js,css,scss,sass}] 13 | indent_size = 4 14 | 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | // requires all tests in `project/test/src/components/**/index.js` 2 | const tests = require.context('./src/', false, /index\.js$/); 3 | 4 | tests.keys().forEach(tests); 5 | 6 | // requires all components in `project/src/components/**/index.js` 7 | const components = require.context('../src/', false, /index\.js$/); 8 | 9 | components.keys().forEach(components); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpackMerge = require("webpack-merge") 2 | const commonConfig = require("./webpack.common") 3 | 4 | module.exports = (env) => { 5 | if (env.destination === 'demo') { 6 | const envConfig = require(`./webpack.${env.destination}`) 7 | return webpackMerge({mode: env.mode}, commonConfig, envConfig) 8 | } 9 | const envConfig = require(`./webpack.${env.mode}`) 10 | return webpackMerge({mode: env.mode}, commonConfig, envConfig) 11 | } 12 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: { 3 | BeerSlider: './src/index.js', 4 | }, 5 | output: { 6 | filename: '[name].js', 7 | library: '[name]', 8 | libraryExport: 'default', 9 | libraryTarget: 'umd' 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.js$/, 15 | exclude: /node_modules/, 16 | use: { 17 | loader: "babel-loader", 18 | options: { presets: ["env"] } 19 | } 20 | } 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /webpack.development.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const HTMLWebpackPlugin = require("html-webpack-plugin") 3 | module.exports = { 4 | module: { 5 | rules: [ 6 | { 7 | test: /\.scss$/, 8 | use: [ 9 | "style-loader", 10 | "css-loader", 11 | "sass-loader" 12 | ] 13 | } 14 | ] 15 | }, 16 | plugins : [ 17 | new HTMLWebpackPlugin({ 18 | inject: false, 19 | title: 'BeerSlider', 20 | template: path.resolve(__dirname, 'src', 'index.html') 21 | }) 22 | ] 23 | } -------------------------------------------------------------------------------- /webpack.test.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | output: { 3 | library: 'BeerSlider', 4 | libraryExport: 'default', 5 | libraryTarget: 'umd' 6 | }, 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.js$/, 11 | use: { 12 | loader: 'istanbul-instrumenter-loader', 13 | options: { esModules: true } 14 | }, 15 | enforce: 'post', 16 | exclude: /(node_modules|tests)/ 17 | }, 18 | { 19 | test: /\.scss$/, 20 | use: [ 21 | "css-loader", 22 | "sass-loader" 23 | ] 24 | } 25 | ] 26 | } 27 | } -------------------------------------------------------------------------------- /webpack.production.js: -------------------------------------------------------------------------------- 1 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 2 | const webpack = require('webpack'); 3 | const UnminifiedWebpackPlugin = require('unminified-webpack-plugin'); 4 | const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); 5 | 6 | module.exports = { 7 | plugins: [ 8 | new MiniCssExtractPlugin(), 9 | new OptimizeCSSAssetsPlugin({ 10 | assetNameRegExp: /[^((?!unmin))]\.css$/g, 11 | }), 12 | new UnminifiedWebpackPlugin({ 13 | postfix: 'unmin' 14 | }) 15 | ], 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.scss$/, 20 | exclude: [/node_modules/], 21 | use: [ 22 | MiniCssExtractPlugin.loader, 23 | "css-loader", 24 | "sass-loader" 25 | ] 26 | } 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /webpack.demo.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const HTMLWebpackPlugin = require("html-webpack-plugin") 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin") 4 | 5 | module.exports = { 6 | mode: 'production', 7 | output: { 8 | path: path.resolve(__dirname, 'demo'), 9 | library: '[name]', 10 | libraryExport: 'default', 11 | libraryTarget: 'umd' 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.scss$/, 17 | exclude: /node_modules/, 18 | use: [ 19 | MiniCssExtractPlugin.loader, 20 | "css-loader", 21 | "sass-loader" 22 | ] 23 | } 24 | ] 25 | }, 26 | plugins : [ 27 | new HTMLWebpackPlugin({ 28 | filename: path.resolve(__dirname, 'demo', 'index.html'), 29 | template: path.resolve(__dirname, 'src', 'index.html'), 30 | inject: false, 31 | title: 'BeerSlider' 32 | }), 33 | new MiniCssExtractPlugin() 34 | ] 35 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "jasmine": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "ecmaVersion": 2015, 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "indent": [ 15 | "error", 16 | 4 17 | ], 18 | "linebreak-style": [ 19 | "warn", 20 | "unix" 21 | ], 22 | "quotes": [ 23 | "warn", 24 | "single" 25 | ], 26 | "semi": [ 27 | "error", 28 | "never" 29 | ], 30 | "no-console": ["error", { allow: ["warn", "error"] }] 31 | }, 32 | "overrides": [ 33 | { 34 | "files": ["src/index.js"], 35 | "rules": { 36 | "no-unused-vars": "off" 37 | } 38 | } 39 | ] 40 | }; 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2018 PeHaa Hetman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /dist/BeerSlider.css: -------------------------------------------------------------------------------- 1 | .beer-slider{display:inline-block;overflow:hidden;position:relative}.beer-slider *,.beer-slider:after,.beer-slider :after,.beer-slider:before,.beer-slider :before{box-sizing:border-box}.beer-slider img,.beer-slider svg{vertical-align:bottom}.beer-slider>*{height:100%}.beer-slider>img{height:auto;max-width:100%}.beer-reveal{left:0;opacity:0;overflow:hidden;position:absolute;right:50%;top:0;transition:opacity .35s;z-index:1}.beer-reveal>:first-child{height:100%;max-width:none;width:200%}.beer-reveal>img:first-child{height:auto}.beer-range{-moz-appearance:none;-ms-touch-action:auto;-webkit-appearance:slider-horizontal!important;bottom:0;cursor:pointer;height:100%;left:-1px;margin:0;opacity:0;position:absolute;top:0;touch-action:auto;width:calc(100% + 2px);z-index:2}.beer-range::-webkit-slider-thumb{-webkit-appearance:none;height:300vh}.beer-range::-moz-range-thumb{-webkit-appearance:none;height:300vh}.beer-range::-ms-tooltip{display:none}.beer-handle{background:hsla(0,0%,100%,.5);border-radius:50%;box-shadow:0 0 6px transparent;color:#000;height:48px;left:50%;opacity:0;pointer-events:none;position:absolute;top:50%;transform:translate3d(-50%,-50%,0);transition:background .3s,box-shadow .3s,opacity .5s .25s;width:48px;z-index:2}.beer-handle:after,.beer-handle:before{border-left:2px solid;border-top:2px solid;content:"";height:10px;position:absolute;top:50%;transform-origin:0 0;width:10px}.beer-handle:before{left:10px;transform:rotate(-45deg)}.beer-handle:after{right:0;transform:rotate(135deg)}.beer-range:focus~.beer-handle{background:hsla(0,0%,100%,.85);box-shadow:0 0 3px rgba(0,0,0,.4)}.beer-reveal[data-beer-label]:after,.beer-slider[data-beer-label]:after{background:hsla(0,0%,100%,.75);border-radius:.125rem;content:attr(data-beer-label);line-height:1;padding:.5rem;position:absolute;top:1.5rem}.beer-slider[data-beer-label]:after{right:1.5rem}.beer-reveal[data-beer-label]:after{left:1.5rem}.beer-reveal[data-beer-label=""]:after,.beer-slider[data-beer-label=""]:after{content:none}.beer-ready .beer-handle,.beer-ready .beer-reveal{opacity:1} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beerslider", 3 | "version": "1.0.3", 4 | "description": "A small,accessible, vanilla JS before-after slider.", 5 | "main": "./dist/BeerSlider.js", 6 | "scripts": { 7 | "preprod": "rimraf dist", 8 | "predemo": "rimraf demo", 9 | "test": "karma start", 10 | "singletest": "karma start --single-run", 11 | "report-coverage": "cat ./coverage/lcov.info | codecov", 12 | "webpack": "webpack", 13 | "webpack-dev-server": "webpack-dev-server", 14 | "dev": "npm run webpack-dev-server -- --env.mode development --hot", 15 | "prod": "npm run webpack -- --env.mode production", 16 | "demo": "npm run webpack -- --env.mode production --env.destination demo", 17 | "build": "npm run prod && npm run demo", 18 | "lint": "eslint tests/src src --fix", 19 | "validate": "npm-run-all lint singletest" 20 | }, 21 | "files": [ 22 | "/dist" 23 | ], 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/pehaa/beerslider.git" 27 | }, 28 | "keywords": [ 29 | "vanilla", 30 | "js", 31 | "slider", 32 | "before-after", 33 | "comparison", 34 | "ui" 35 | ], 36 | "author": "PeHaa Hetman (http://wptemplates.pehaa.com/)", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/pehaa/beerslider/issues" 40 | }, 41 | "homepage": "https://github.com/pehaa/beerslider#readme", 42 | "devDependencies": { 43 | "babel-core": "^6.26.3", 44 | "babel-loader": "^7.1.5", 45 | "babel-preset-env": "^1.7.0", 46 | "bulma": "^0.7.1", 47 | "codecov": "^3.0.4", 48 | "css-loader": "^1.0.0", 49 | "eslint": "^5.4.0", 50 | "eslint-config-standard": "^11.0.0", 51 | "eslint-plugin-import": "^2.14.0", 52 | "eslint-plugin-node": "^7.0.1", 53 | "eslint-plugin-promise": "^4.0.0", 54 | "eslint-plugin-standard": "^3.1.0", 55 | "ghooks": "^2.0.4", 56 | "html-webpack-plugin": "^3.2.0", 57 | "istanbul-instrumenter-loader": "^3.0.1", 58 | "karma": "^3.0.0", 59 | "karma-coverage-istanbul-reporter": "^2.0.1", 60 | "mini-css-extract-plugin": "^0.4.1", 61 | "node-sass": "^4.9.3", 62 | "npm-run-all": "^4.1.3", 63 | "optimize-css-assets-webpack-plugin": "^5.0.0", 64 | "rimraf": "^2.6.2", 65 | "sass-loader": "^7.1.0", 66 | "style-loader": "^0.22.1", 67 | "unminified-webpack-plugin": "^2.0.0", 68 | "webpack": "^4.16.5", 69 | "webpack-cli": "^3.1.0", 70 | "webpack-dev-server": "^3.1.5", 71 | "webpack-karma-jasmine": "^3.0.0", 72 | "webpack-merge": "^4.1.4" 73 | }, 74 | "dependencies": { 75 | "core-js": "^3.0.0-beta.3" 76 | }, 77 | "config": { 78 | "ghooks": { 79 | "pre-commit": "npm run validate" 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /demo-assets/demo.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: Montserrat, sans-serif; 3 | line-height: 1.5; 4 | color: #4a4a4a; 5 | font-weight: 300; 6 | } 7 | body { 8 | margin: 0; 9 | } 10 | header{ 11 | text-align: center; 12 | overflow: hidden; 13 | } 14 | p { 15 | margin: 0; } 16 | .container { 17 | max-width: 960px; 18 | margin: 0 auto; 19 | padding: 0 0.75rem; 20 | } 21 | .heading-font { 22 | font-family: "Six Caps", sans-serif; 23 | font-weight: 400; 24 | letter-spacing: 2px; } 25 | a { 26 | text-decoration: none; 27 | color: #ef5f52; 28 | transition: 0.5s; } 29 | h1 { 30 | font-size: 4rem; 31 | margin: 0; 32 | } 33 | h2 { 34 | margin: 0 0 1.5rem; 35 | } 36 | h1 + h2 { 37 | font-weight: 300; 38 | } 39 | code { 40 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 41 | } 42 | section h2, section h3, a { 43 | color: #ef5f52; 44 | } 45 | section a { 46 | border-bottom: 1px solid; 47 | } 48 | section a:hover { 49 | color: #000; 50 | } 51 | 52 | .mb { 53 | margin-bottom: 1.5rem!important; 54 | } 55 | 56 | h2.heading-font { 57 | font-size: 3rem; 58 | } 59 | h3.heading-font { 60 | font-size: 2rem; 61 | } 62 | .beer-slider { 63 | margin-bottom: 1.5rem; 64 | } 65 | .beer-slider[data-beer-label]:after, 66 | .beer-reveal[data-beer-label]:after { 67 | text-transform: uppercase; 68 | letter-spacing: 2px; 69 | font-size: 0.75rem; 70 | } 71 | 72 | .fork-me { 73 | position: absolute; 74 | top: 0; 75 | right:0; 76 | overflow: hidden; 77 | width: 180px; 78 | height: 180px; 79 | } 80 | 81 | .fork-me-link { 82 | display: inline-block; 83 | position: absolute; 84 | top: 0; 85 | right: 0; 86 | background-color: #ef5f52; 87 | transform-origin: left top; 88 | transform: translate3d(74px, 0%, 0) rotate(45deg) translate3d(0, -100%, 0); 89 | transition: 0.5s; 90 | text-align: center; 91 | color: #fff; 92 | padding: 10px 50px; 93 | overflow: hidden; 94 | white-space: nowrap; 95 | } 96 | 97 | .fork-me-link:hover, .fork-me-link:focus { 98 | background-color: #000; 99 | outline: none; 100 | } 101 | .beer-slider { 102 | margin-left: auto; 103 | margin-right: auto; 104 | } 105 | @media screen and (min-width: 768px) { 106 | .examples { 107 | display: flex; 108 | flex-wrap: wrap; 109 | justify-content: space-between; 110 | } 111 | .examples .beer-slider { 112 | max-width: calc(50% - 12px); 113 | margin-left: 0; 114 | margin-right: 0; 115 | } 116 | } 117 | 118 | .examples h2 { 119 | width: 100%; 120 | } 121 | .how { 122 | background: #f5eeee; 123 | padding: 1.5rem 0; 124 | } 125 | .container pre { 126 | background: #f5eeee; 127 | } 128 | footer { 129 | border-top: 4px solid #ef5f52; 130 | padding: 1.5rem 0; 131 | 132 | } 133 | footer .container { 134 | display: flex; 135 | justify-content: space-between; 136 | align-items: center; 137 | } 138 | header img { 139 | margin-top: 1.5rem; 140 | } -------------------------------------------------------------------------------- /demo-assets/prism/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | background: none; 12 | text-shadow: 0 1px white; 13 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | word-wrap: normal; 19 | line-height: 1.5; 20 | 21 | -moz-tab-size: 4; 22 | -o-tab-size: 4; 23 | tab-size: 4; 24 | 25 | -webkit-hyphens: none; 26 | -moz-hyphens: none; 27 | -ms-hyphens: none; 28 | hyphens: none; 29 | } 30 | 31 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 32 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 33 | text-shadow: none; 34 | background: #b3d4fc; 35 | } 36 | 37 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 38 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 39 | text-shadow: none; 40 | background: #b3d4fc; 41 | } 42 | 43 | @media print { 44 | code[class*="language-"], 45 | pre[class*="language-"] { 46 | text-shadow: none; 47 | } 48 | } 49 | 50 | /* Code blocks */ 51 | pre[class*="language-"] { 52 | padding: 1em; 53 | margin: .5em 0; 54 | overflow: auto; 55 | } 56 | 57 | :not(pre) > code[class*="language-"], 58 | pre[class*="language-"] { 59 | background: #f5f2f0; 60 | } 61 | 62 | /* Inline code */ 63 | :not(pre) > code[class*="language-"] { 64 | padding: .1em; 65 | border-radius: .3em; 66 | white-space: normal; 67 | } 68 | 69 | .token.comment, 70 | .token.prolog, 71 | .token.doctype, 72 | .token.cdata { 73 | color: slategray; 74 | } 75 | 76 | .token.punctuation { 77 | color: #999; 78 | } 79 | 80 | .namespace { 81 | opacity: .7; 82 | } 83 | 84 | .token.property, 85 | .token.tag, 86 | .token.boolean, 87 | .token.number, 88 | .token.constant, 89 | .token.symbol, 90 | .token.deleted { 91 | color: #905; 92 | } 93 | 94 | .token.selector, 95 | .token.attr-name, 96 | .token.string, 97 | .token.char, 98 | .token.builtin, 99 | .token.inserted { 100 | color: #690; 101 | } 102 | 103 | .token.operator, 104 | .token.entity, 105 | .token.url, 106 | .language-css .token.string, 107 | .style .token.string { 108 | color: #a67f59; 109 | background: hsla(0, 0%, 100%, .5); 110 | } 111 | 112 | .token.atrule, 113 | .token.attr-value, 114 | .token.keyword { 115 | color: #07a; 116 | } 117 | 118 | .token.function { 119 | color: #DD4A68; 120 | } 121 | 122 | .token.regex, 123 | .token.important, 124 | .token.variable { 125 | color: #e90; 126 | } 127 | 128 | .token.important, 129 | .token.bold { 130 | font-weight: bold; 131 | } 132 | .token.italic { 133 | font-style: italic; 134 | } 135 | 136 | .token.entity { 137 | cursor: help; 138 | } 139 | 140 | -------------------------------------------------------------------------------- /demo/BeerSlider.css: -------------------------------------------------------------------------------- 1 | .beer-slider { 2 | display: inline-block; 3 | position: relative; 4 | overflow: hidden; } 5 | .beer-slider *, .beer-slider:before, .beer-slider:after, 6 | .beer-slider *:before, 7 | .beer-slider *:after { 8 | box-sizing: border-box; } 9 | .beer-slider img, .beer-slider svg { 10 | vertical-align: bottom; } 11 | .beer-slider > * { 12 | height: 100%; } 13 | .beer-slider > img { 14 | max-width: 100%; 15 | height: auto; } 16 | 17 | .beer-reveal { 18 | position: absolute; 19 | left: 0; 20 | top: 0; 21 | right: 50%; 22 | overflow: hidden; 23 | z-index: 1; 24 | opacity: 0; 25 | transition: opacity 0.35s; } 26 | .beer-reveal > :first-child { 27 | width: 200%; 28 | max-width: none; 29 | height: 100%; } 30 | .beer-reveal > img:first-child { 31 | height: auto; } 32 | 33 | .beer-range { 34 | position: absolute; 35 | z-index: 2; 36 | top: 0; 37 | bottom: 0; 38 | height: 100%; 39 | margin: 0; 40 | left: -1px; 41 | width: calc(100% + 2px); 42 | cursor: pointer; 43 | -webkit-appearance: slider-horizontal !important; 44 | -moz-appearance: none; 45 | opacity: 0; 46 | -ms-touch-action: auto; 47 | touch-action: auto; } 48 | .beer-range::-webkit-slider-thumb { 49 | -webkit-appearance: none; 50 | height: 300vh; } 51 | .beer-range::-moz-range-thumb { 52 | -webkit-appearance: none; 53 | height: 300vh; } 54 | .beer-range::-ms-tooltip { 55 | display: none; } 56 | 57 | .beer-handle { 58 | position: absolute; 59 | z-index: 2; 60 | pointer-events: none; 61 | opacity: 0; 62 | top: 50%; 63 | left: 50%; 64 | transform: translate3d(-50%, -50%, 0); 65 | color: #000; 66 | background: rgba(255, 255, 255, 0.5); 67 | width: 48px; 68 | height: 48px; 69 | border-radius: 50%; 70 | box-shadow: 0 0 6px rgba(0, 0, 0, 0); 71 | transition: background 0.3s, box-shadow 0.3s, opacity 0.5s 0.25s; } 72 | .beer-handle:before, .beer-handle:after { 73 | content: ''; 74 | position: absolute; 75 | width: 10px; 76 | height: 10px; 77 | top: 50%; 78 | border-top: solid 2px; 79 | border-left: solid 2px; 80 | transform-origin: 0 0; } 81 | .beer-handle:before { 82 | left: 10px; 83 | transform: rotate(-45deg); } 84 | .beer-handle:after { 85 | right: 0; 86 | transform: rotate(135deg); } 87 | 88 | .beer-range:focus ~ .beer-handle { 89 | background: rgba(255, 255, 255, 0.85); 90 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.4); } 91 | 92 | .beer-slider[data-beer-label]:after, 93 | .beer-reveal[data-beer-label]:after { 94 | content: attr(data-beer-label); 95 | position: absolute; 96 | top: 1.5rem; 97 | line-height: 1; 98 | padding: 0.5rem; 99 | border-radius: 0.125rem; 100 | background: rgba(255, 255, 255, 0.75); } 101 | 102 | .beer-slider[data-beer-label]:after { 103 | right: 1.5rem; } 104 | 105 | .beer-reveal[data-beer-label]:after { 106 | left: 1.5rem; } 107 | 108 | .beer-slider[data-beer-label=""]:after, 109 | .beer-reveal[data-beer-label=""]:after { 110 | content: none; } 111 | 112 | .beer-ready .beer-reveal, .beer-ready .beer-handle { 113 | opacity: 1; } 114 | 115 | -------------------------------------------------------------------------------- /dist/BeerSlider.unmin.css: -------------------------------------------------------------------------------- 1 | .beer-slider { 2 | display: inline-block; 3 | position: relative; 4 | overflow: hidden; } 5 | .beer-slider *, .beer-slider:before, .beer-slider:after, 6 | .beer-slider *:before, 7 | .beer-slider *:after { 8 | box-sizing: border-box; } 9 | .beer-slider img, .beer-slider svg { 10 | vertical-align: bottom; } 11 | .beer-slider > * { 12 | height: 100%; } 13 | .beer-slider > img { 14 | max-width: 100%; 15 | height: auto; } 16 | 17 | .beer-reveal { 18 | position: absolute; 19 | left: 0; 20 | top: 0; 21 | right: 50%; 22 | overflow: hidden; 23 | z-index: 1; 24 | opacity: 0; 25 | transition: opacity 0.35s; } 26 | .beer-reveal > :first-child { 27 | width: 200%; 28 | max-width: none; 29 | height: 100%; } 30 | .beer-reveal > img:first-child { 31 | height: auto; } 32 | 33 | .beer-range { 34 | position: absolute; 35 | z-index: 2; 36 | top: 0; 37 | bottom: 0; 38 | height: 100%; 39 | margin: 0; 40 | left: -1px; 41 | width: calc(100% + 2px); 42 | cursor: pointer; 43 | -webkit-appearance: slider-horizontal !important; 44 | -moz-appearance: none; 45 | opacity: 0; 46 | -ms-touch-action: auto; 47 | touch-action: auto; } 48 | .beer-range::-webkit-slider-thumb { 49 | -webkit-appearance: none; 50 | height: 300vh; } 51 | .beer-range::-moz-range-thumb { 52 | -webkit-appearance: none; 53 | height: 300vh; } 54 | .beer-range::-ms-tooltip { 55 | display: none; } 56 | 57 | .beer-handle { 58 | position: absolute; 59 | z-index: 2; 60 | pointer-events: none; 61 | opacity: 0; 62 | top: 50%; 63 | left: 50%; 64 | transform: translate3d(-50%, -50%, 0); 65 | color: #000; 66 | background: rgba(255, 255, 255, 0.5); 67 | width: 48px; 68 | height: 48px; 69 | border-radius: 50%; 70 | box-shadow: 0 0 6px rgba(0, 0, 0, 0); 71 | transition: background 0.3s, box-shadow 0.3s, opacity 0.5s 0.25s; } 72 | .beer-handle:before, .beer-handle:after { 73 | content: ''; 74 | position: absolute; 75 | width: 10px; 76 | height: 10px; 77 | top: 50%; 78 | border-top: solid 2px; 79 | border-left: solid 2px; 80 | transform-origin: 0 0; } 81 | .beer-handle:before { 82 | left: 10px; 83 | transform: rotate(-45deg); } 84 | .beer-handle:after { 85 | right: 0; 86 | transform: rotate(135deg); } 87 | 88 | .beer-range:focus ~ .beer-handle { 89 | background: rgba(255, 255, 255, 0.85); 90 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.4); } 91 | 92 | .beer-slider[data-beer-label]:after, 93 | .beer-reveal[data-beer-label]:after { 94 | content: attr(data-beer-label); 95 | position: absolute; 96 | top: 1.5rem; 97 | line-height: 1; 98 | padding: 0.5rem; 99 | border-radius: 0.125rem; 100 | background: rgba(255, 255, 255, 0.75); } 101 | 102 | .beer-slider[data-beer-label]:after { 103 | right: 1.5rem; } 104 | 105 | .beer-reveal[data-beer-label]:after { 106 | left: 1.5rem; } 107 | 108 | .beer-slider[data-beer-label=""]:after, 109 | .beer-reveal[data-beer-label=""]:after { 110 | content: none; } 111 | 112 | .beer-ready .beer-reveal, .beer-ready .beer-handle { 113 | opacity: 1; } 114 | 115 | -------------------------------------------------------------------------------- /src/beerslider.js: -------------------------------------------------------------------------------- 1 | export class BeerSlider { 2 | 3 | constructor (element, {start = '50', prefix = 'beer'} = {}) { 4 | this.start = parseInt(start) ? Math.min(100, Math.max(0, parseInt(start))) : 50 5 | this.prefix = prefix 6 | if (!element || element.children.length !== 2) { 7 | return 8 | } 9 | this.element = element 10 | this.revealContainer = this.element.children[1] 11 | if (this.revealContainer.children.length < 1) { 12 | return 13 | } 14 | this.revealElement = this.revealContainer.children[0] 15 | this.range = this.addElement('input', { 16 | type: 'range', 17 | class: `${this.prefix}-range`, 18 | 'aria-label': 'Percent of revealed content', 19 | 'aria-valuemin': '0', 20 | 'aria-valuemax': '100', 21 | 'aria-valuenow': this.start, 22 | value: this.start, 23 | min: '0', 24 | max: '100' 25 | }) 26 | this.handle = this.addElement('span', { 27 | class: `${this.prefix}-handle` 28 | }) 29 | this.onImagesLoad() 30 | } 31 | init () { 32 | this.element.classList.add(`${this.prefix}-ready`) 33 | this.setImgWidth() 34 | this.move() 35 | this.addListeners() 36 | } 37 | loadingImg (src) { 38 | return new Promise( (resolve, reject) => { 39 | if (!src) { 40 | resolve() 41 | } 42 | const img = new Image() 43 | img.onload = () => resolve() 44 | img.onerror = () => reject() 45 | img.src = src 46 | }) 47 | } 48 | loadedBoth () { 49 | const mainImageSrc = this.element.children[0].src || this.element.children[0].getAttribute(`data-${this.prefix}-src`) 50 | const revealImageSrc = this.revealElement.src || this.revealElement.getAttribute(`data-${this.prefix}-src`) 51 | return Promise.all([this.loadingImg(mainImageSrc), this.loadingImg(revealImageSrc)]) 52 | } 53 | onImagesLoad () { 54 | if ( !this.revealElement ) { 55 | return 56 | } 57 | this.loadedBoth().then( 58 | () => { 59 | this.init() 60 | }, 61 | () => { 62 | console.error('Some errors occurred and images are not loaded.') 63 | } 64 | ) 65 | } 66 | addElement (tag, attributes) { 67 | const el = document.createElement(tag) 68 | Object.keys(attributes).forEach( (key) => { 69 | el.setAttribute(key, attributes[key]) 70 | }) 71 | this.element.appendChild(el) 72 | return el 73 | } 74 | setImgWidth () { 75 | this.revealElement.style.width = getComputedStyle(this.element)['width'] 76 | } 77 | addListeners () { 78 | const eventTypes = ['input', 'change'] 79 | eventTypes.forEach( (i) => { 80 | this.range.addEventListener( i, () => {this.move()} ) 81 | }) 82 | window.addEventListener('resize', () => {this.setImgWidth()}) 83 | } 84 | move () { 85 | this.revealContainer.style.width = `${this.range.value}%` 86 | this.handle.style.left = `${this.range.value}%` 87 | this.range.setAttribute('aria-valuenow', this.range.value) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/style.scss: -------------------------------------------------------------------------------- 1 | $prefix : "beer"; 2 | $initial-reveal: 50%; 3 | 4 | .#{$prefix}-slider { 5 | *, 6 | &:before, 7 | &:after, 8 | *:before, 9 | *:after { 10 | box-sizing: border-box; 11 | } 12 | display: inline-block; 13 | position: relative; 14 | overflow: hidden; 15 | 16 | img, svg { 17 | vertical-align: bottom; 18 | } 19 | & > * { 20 | height: 100%; 21 | } 22 | & > img { 23 | max-width: 100%; 24 | height: auto; 25 | } 26 | } 27 | .#{$prefix}-reveal { 28 | position: absolute; 29 | left: 0; 30 | top: 0; 31 | right: 100% - $initial-reveal; 32 | overflow: hidden; 33 | z-index: 1; 34 | opacity: 0; 35 | transition: opacity 0.35s; 36 | & > :first-child { 37 | width: 100%*100%/$initial-reveal; 38 | max-width: none; 39 | height: 100%; 40 | } 41 | & > img:first-child { 42 | height: auto; 43 | } 44 | } 45 | 46 | .#{$prefix}-range { 47 | position: absolute; 48 | z-index: 2; 49 | top: 0; 50 | bottom: 0; 51 | height: 100%; 52 | margin: 0; 53 | left: -1px; 54 | width: calc(100% + 2px); 55 | cursor: pointer; 56 | -webkit-appearance: slider-horizontal!important; 57 | -moz-appearance:none; 58 | opacity: 0; 59 | -ms-touch-action: auto; 60 | touch-action: auto; 61 | 62 | &::-webkit-slider-thumb { 63 | -webkit-appearance:none; 64 | height: 300vh; 65 | } 66 | &::-moz-range-thumb { 67 | -webkit-appearance:none; 68 | height: 300vh; 69 | } 70 | &::-ms-tooltip { 71 | display: none; 72 | } 73 | } 74 | 75 | 76 | .#{$prefix}-handle { 77 | position: absolute; 78 | z-index: 2; 79 | pointer-events: none; 80 | opacity: 0; 81 | top: 50%; 82 | left: 50%; 83 | transform: translate3d( -50%, -50%, 0); 84 | color: #000; 85 | background: rgba(255,255,255,.5); 86 | width: 48px; 87 | height: 48px; 88 | border-radius: 50%; 89 | box-shadow: 0 0 6px rgba(0, 0, 0, 0); 90 | transition: background 0.3s, box-shadow 0.3s, opacity 0.5s 0.25s; 91 | 92 | &:before, 93 | &:after { 94 | content: ''; 95 | position: absolute; 96 | width: 10px; 97 | height: 10px; 98 | top: 50%; 99 | border-top: solid 2px; 100 | border-left: solid 2px; 101 | transform-origin: 0 0; 102 | } 103 | &:before { 104 | left: 10px; 105 | transform: rotate(-45deg); 106 | } 107 | &:after { 108 | right: 0; 109 | transform: rotate( 135deg); 110 | } 111 | } 112 | 113 | .#{$prefix}-range:focus ~ .#{$prefix}-handle { 114 | background: rgba(255,255,255,.85); 115 | box-shadow: 0 0 3px rgba(0, 0, 0, .4); 116 | } 117 | 118 | .#{$prefix}-slider[data-beer-label]:after, 119 | .#{$prefix}-reveal[data-beer-label]:after { 120 | content: attr(data-beer-label); 121 | position: absolute; 122 | top: 1.5rem; 123 | line-height: 1; 124 | padding: 0.5rem; 125 | border-radius: 0.125rem; 126 | background: rgba(255,255,255,.75); 127 | } 128 | .#{$prefix}-slider[data-beer-label]:after { 129 | right: 1.5rem; 130 | } 131 | .#{$prefix}-reveal[data-beer-label]:after { 132 | left: 1.5rem; 133 | white-space: nowrap; 134 | } 135 | .#{$prefix}-slider[data-beer-label=""]:after, 136 | .#{$prefix}-reveal[data-beer-label=""]:after { 137 | content: none; 138 | } 139 | 140 | .#{$prefix}-ready { 141 | .#{$prefix}-reveal, .#{$prefix}-handle { 142 | opacity: 1; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at info@pehaa.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Travis (.org)](https://img.shields.io/travis/pehaa/beerslider.svg?style=for-the-badge) 2 | ![Codecov](https://img.shields.io/codecov/c/github/pehaa/beerslider.svg?style=for-the-badge) 3 | ![Github file size](https://img.shields.io/github/size/pehaa/beerslider/dist/BeerSlider.js.svg?style=for-the-badge) 4 | 5 | # BeerSlider 6 | 7 | ## A vanilla JavaScript accessible before-after slider 8 | 9 | 10 | ### Demo 11 | You can find [the demo here](https://pepsized.com/wp-content/uploads/2018/09/beerslider/demo/index.html). 12 | 13 | ## How? 14 | 15 | 16 | ## Installation 17 | 18 | The ```dist``` folder contains the ready for production minified files: ```BeerSlider.js``` and ```BeerSlider.css``` 19 | ```html 20 | 21 | ... 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | ``` 33 | 34 | You can also use the cdn solution 35 | ```html 36 | 37 | ... 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | ``` 49 | 50 | 51 | #### npm: 52 | ```bash 53 | npm install beerslider 54 | ``` 55 | 56 | ## Usage: 57 | 58 | The basic markup is: 59 | 60 | ```html 61 |
62 | 63 |
64 | 65 |
66 |
67 | ``` 68 | The data-beer-labels are optional, you can leave them empty or do not add them at all. 69 | 70 | To activate the slider add the following: 71 | ```js 72 | new BeerSlider(document.getElementById('slider')); 73 | ``` 74 | 75 | ### Customization 76 | 77 | You can initiate BeerSlider with some options, the available options are: 78 | ```js 79 | { 80 | // start value 81 | start: '50', 82 | // prefix 83 | prefix: 'beer' 84 | } 85 | ``` 86 | ### Use with jQuery or Zepto 87 | 88 | If you use jQuery or Zepto in your project and have a few before-after sliders on your page, you can do something like that: 89 | ```html 90 | 99 | ``` 100 | or: 101 | ```html 102 | 113 | ``` 114 | with: 115 | ```html 116 |
117 | Original - Man holding beer 118 |
119 | Processed - Man holding beer 120 |
121 |
122 |
123 | Original - Man holding beer 124 |
125 | Processed - Man holding beer 126 |
127 |
128 | ``` 129 | A Codepen demo using jQuery is available [here,](https://codepen.io/pehaa/pen/PdJqPE) 130 | and Zepto [here.](https://codepen.io/pehaa/pen/EebMgg) 131 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('./webpack.test.js'); 2 | 3 | module.exports = function(config) { 4 | config.set({ 5 | //root path location to resolve paths defined in files and exclude 6 | basePath: '', 7 | //files/patterns to exclude from loaded files 8 | exclude: [], 9 | //files/patterns to load in the browser 10 | files: [ 11 | 'tests/index.js' 12 | /*parameters*/ 13 | //watched: if autoWatch is true all files that have set watched to true will be watched for changes 14 | //served: should the files be served by Karma's webserver? 15 | //included: should the files be included in the browser using 155 | 156 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= htmlWebpackPlugin.options.title %> 6 | 7 | <% if(htmlWebpackPlugin.files.css) { %> 8 | 9 | <% } %> 10 | 11 | 12 | 13 | 14 | 15 |
Fork Me On GitHub
16 |
17 |
18 | 19 |

Beer Slider

20 |

Responsive & Accessible Before-After Slider

21 |
22 |
23 | Original man holding beer 24 |
25 | Processed with logo and Lightroom presets 26 |
27 |
28 |
29 |

What's Beer Plugin?

30 |

Beer Slider is a lightweight vanilla JavaScript plugin.

31 |

It's basic purpose is to compare two versions of an image, for example the same object shot in two different moments, a pre-edited photo and its processed version, a sketch and the finished illustration, etc.

32 |

It can be particularly useful with photo presets (Lightroom presets, Photoshop actions, etc.).

33 |

Beer Slider is keyboard accessible - you can access it and reveal/hide the "after" state image with the keyboard.

34 |
35 |
36 |
37 |
38 |
39 |

Examples:

40 |
41 | Baltic seashore - unprocessed original photo 42 |
43 | Baltic seashore - sepicol Lightroom preset by Altphotos.com 44 |
45 |
46 |
47 | Dogs playing in the sea - unprocessed original photo 48 |
49 | Dogs playing in the sea using Altphotos.com Lightroom presets 50 |
51 |
52 |
53 |
54 | Original unprocessed panoramic photo with mountainous landscape 55 |
56 | Panoramic photo with mountainous landscape and some Lightroom presets applier 57 |
58 |
59 |
60 |
61 |
62 |

How To Use?

63 |

Include the stylesheet in your head:

64 |

 65 | <link rel="stylesheet" href="BeerSlider.css">
 66 |       
67 |

Structure your markup as in the example below. Make sure to keep the class names as in the example. The data-beer-label attributes define the text of the labels displayed in the upper corners of the slider. Remove them or leave empty if you do not need the label(s).

68 |

 69 | <div id="beer-slider" class="beer-slider" data-beer-label="before">
 70 |   <img src="man-hold-beer.jpg" alt="Original - Man holding beer">
 71 |   <div class="beer-reveal" data-beer-label="after">
 72 |     <img src="man-hold-beer-after.jpg" alt="Processed - Man holding beer">
 73 |   </div>
 74 | </div>
 75 |       
76 |

Include the script before the closing body tag:

77 |

 78 | <script src="BeerSlider.js"></script>
 79 | <script>
 80 |   var slider = new BeerSlider( document.getElementById( "beer-slider" ) );
 81 | </script>
 82 |       
83 |

84 | You can also set the starting position somewhere else than in the middle using the parameter start taking values from 0 to 100. 85 |

86 |

 87 | <script>
 88 |   new BeerSlider( document.getElementById( "beer-slider" ), { start: 25 } );
 89 | </script>
 90 |       
91 |

Use with jQuery or Zepto

92 |

If you use jQuery or Zepto in your project and have a few before-after sliders on your page, you can do something like that:

93 |

 94 | <script>
 95 |   $.fn.BeerSlider = function ( options ) {
 96 |     options = options || {};
 97 |     return this.each(function() {
 98 |       new BeerSlider(this, options);
 99 |     });
100 |   };
101 |   $('.beer-slider').BeerSlider({start: 25});
102 | </script>
103 |       
104 |
or:
105 |

106 | <script>
107 |   $.fn.BeerSlider = function ( options ) {
108 |     options = options || {};
109 |     return this.each(function() {
110 |       new BeerSlider(this, options);
111 |     });
112 |   };
113 |   $('.beer-slider').each( (function( index, el ) {
114 |     $(el).BeerSlider({start: $(el).data('beer-start')})
115 |   });
116 | </script>
117 |       
118 |
with:
119 |

120 | <div id="beer-slider" class="beer-slider" data-beer-label="before" data-beer-start="25">
121 |   <img src="man-hold-beer.jpg" alt="Original - Man holding beer">
122 |   <div class="beer-reveal" data-beer-label="after">
123 |     <img src="man-hold-beer-after.jpg" alt="Processed - Man holding beer">
124 |   </div>
125 | </div>
126 | <div id="beer-slider2" class="beer-slider" data-beer-label="before" data-beer-start="75">
127 |   <img src="man-hold-beer.jpg" alt="Original - Man holding beer">
128 |   <div class="beer-reveal" data-beer-label="after">
129 |     <img src="man-hold-beer-after.jpg" alt="Processed - Man holding beer">
130 |   </div>
131 | </div>
132 |       
133 |
134 |
135 | 136 |
137 |
138 |

Credits:

139 |

In this demo we use the beautiful presets from the free pack of Lightroom presets available on Altphotos as well as the CC0 images found on Altphotos. 140 |

The original beer photo by Paweł Kadysz can be found on Altphotos. 141 |

Beer Slider logo was created by Joe Vains.

142 |
143 |
144 | 152 | 153 | 154 | 155 | 156 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /demo-assets/prism/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ 2 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={manual:_self.Prism&&_self.Prism.manual,util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(w instanceof s)){h.lastIndex=0;var _=h.exec(w),P=1;if(!_&&m&&b!=t.length-1){if(h.lastIndex=k,_=h.exec(e),!_)break;for(var A=_.index+(d?_[1].length:0),j=_.index+_[0].length,x=b,O=k,S=t.length;S>x&&(j>O||!t[x].type&&!t[x-1].greedy);++x)O+=t[x].length,A>=O&&(++b,k=O);if(t[b]instanceof s||t[x-1].greedy)continue;P=x-b,w=e.slice(k,O),_.index-=k}if(_){d&&(p=_[1].length);var A=_.index+p,_=_[0].slice(p),j=A+_.length,N=w.slice(0,A),C=w.slice(j),E=[b,P];N&&(++b,k+=N.length,E.push(N));var L=new s(u,f?n.tokenize(_,f):_,y,_,m);if(E.push(L),C&&E.push(C),Array.prototype.splice.apply(t,E),1!=P&&n.matchGrammar(e,t,a,b,k,!0,u),l)break}else if(l)break}}}}},tokenize:function(e,t){var a=[e],r=t.rest;if(r){for(var i in r)t[i]=r[i];delete t.rest}return n.matchGrammar(e,a,t,0,0,!1),a},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var i={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}n.hooks.run("wrap",i);var o=Object.keys(i.attributes).map(function(e){return e+'="'+(i.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,i=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,!document.addEventListener||n.manual||r.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); 3 | Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\s\S])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\s\S]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; 4 | Prism.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:{pattern:/("|')(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); 5 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; 6 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[[^\]\r\n]+]|\\.|[^\/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0,greedy:!0}}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\\\|\\?[^\\])*?`/,greedy:!0,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\s\S]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; 7 | -------------------------------------------------------------------------------- /tests/src/index.js: -------------------------------------------------------------------------------- 1 | import BeerSlider from './../../src/index.js' 2 | 3 | const htmlToTest = document.querySelector('body') 4 | const innerHTML = `
5 | 6 |
7 | 8 |
9 |
10 |
11 | 12 |
13 | 14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
` 33 | describe( 'The Beerslider', () => { 34 | it('can be instatiated via constructor', () => { 35 | expect( 36 | () => { new BeerSlider() }).not.toThrow() 37 | expect( 38 | () => { new BeerSlider( document.getElementById('slider123')) }).not.toThrow() 39 | expect( 40 | () => { new BeerSlider( document.getElementById('slider1')) }).not.toThrow() 41 | }) 42 | it('has a start property', () => { 43 | const beerslider = new BeerSlider() 44 | expect(beerslider.start).toBeDefined() 45 | }) 46 | it('has a default value for start property equal to 50', () => { 47 | const beerslider = new BeerSlider() 48 | expect(beerslider.start).toEqual(50) 49 | }) 50 | it('has a prefix property', () => { 51 | const beerslider = new BeerSlider() 52 | expect(beerslider.prefix).toBeDefined() 53 | }) 54 | it('has a default value for prefix property equal "beer"', () => { 55 | const beerslider = new BeerSlider() 56 | expect(beerslider.prefix).toEqual('beer') 57 | }) 58 | it('start and prefix can be overriden', () => { 59 | const beerslider = new BeerSlider(null, {prefix: 'ba', start: 25}) 60 | expect(beerslider.prefix).toEqual('ba') 61 | expect(beerslider.start).toEqual(25) 62 | }) 63 | it('min possible start property is 0', () => { 64 | const beerslider = new BeerSlider(null, {start: -59}) 65 | expect(beerslider.start).toEqual(0) 66 | }) 67 | it('max possible start property is 100', () => { 68 | const beerslider = new BeerSlider(null, {start: '120%'}) 69 | expect(beerslider.start).toEqual(100) 70 | }) 71 | it('should not accept invalid start option', () => { 72 | const beerslider = new BeerSlider(null, {start: 'left'}) 73 | expect(beerslider.start).toEqual(50) 74 | }) 75 | }) 76 | describe( 'The Beerslider constructor on non specified or non existing element', () => { 77 | it('should not have specified element property', () => { 78 | const beerslider = new BeerSlider() 79 | expect(beerslider.element).not.toBeDefined() 80 | const beerslider1 = new BeerSlider(document.getElementById('slider123')) 81 | expect(beerslider1.element).not.toBeDefined() 82 | }) 83 | }) 84 | describe( 'The Beerslider constructor on an existing element', () => { 85 | beforeEach( () => { 86 | htmlToTest.innerHTML = innerHTML 87 | }) 88 | afterEach( () => { 89 | htmlToTest.innerHTML = '' 90 | }) 91 | it('should have specified element, revealContainer and revealElement properties', () => { 92 | const el = document.getElementById('slider1') 93 | const beerslider = new BeerSlider(el) 94 | expect(beerslider.element).toBeDefined() 95 | expect(beerslider.revealContainer).toBeDefined() 96 | expect(beerslider.revealElement).toBeDefined() 97 | expect(beerslider.range).toBeDefined() 98 | expect(beerslider.handle).toBeDefined() 99 | }) 100 | it('should not have revealElement property if revealContainer has no child', () => { 101 | const el = document.getElementById('slider3') 102 | const beerslider = new BeerSlider(el) 103 | expect(beerslider.element).toBeDefined() 104 | expect(beerslider.revealContainer).toBeDefined() 105 | expect(beerslider.revealElement).not.toBeDefined() 106 | expect(beerslider.range).not.toBeDefined() 107 | expect(beerslider.handle).not.toBeDefined() 108 | }) 109 | it('have proper markup for range slider', () => { 110 | const el = document.getElementById('slider1') 111 | const beerslider = new BeerSlider(el) 112 | expect(beerslider.range.tagName).toEqual('INPUT') 113 | expect(beerslider.range.type).toEqual('range') 114 | expect(beerslider.range.min).toEqual('0') 115 | expect(beerslider.range.max).toEqual('100') 116 | expect(beerslider.range.value).toEqual('50') 117 | expect(beerslider.range.getAttribute('aria-valuemin')).toEqual('0') 118 | expect(beerslider.range.getAttribute('aria-valuemax')).toEqual('100') 119 | expect(beerslider.range.getAttribute('aria-valuenow')).toEqual('50') 120 | 121 | expect(beerslider.range.getAttribute('class')).toContain('beer-range') 122 | }) 123 | it('have proper markup for range slider if called with options', () => { 124 | const el = document.getElementById('slider1') 125 | const beerslider = new BeerSlider(el, {prefix: 'ba', start: 42}) 126 | expect(beerslider.range.tagName).toEqual('INPUT') 127 | expect(beerslider.range.type).toEqual('range') 128 | expect(beerslider.range.min).toEqual('0') 129 | expect(beerslider.range.max).toEqual('100') 130 | expect(beerslider.range.value).toEqual('42') 131 | expect(beerslider.range.getAttribute('aria-valuemin')).toEqual('0') 132 | expect(beerslider.range.getAttribute('aria-valuemax')).toEqual('100') 133 | expect(beerslider.range.getAttribute('aria-valuenow')).toEqual('42') 134 | expect(beerslider.range.getAttribute('class')).toContain('ba-range') 135 | }) 136 | it('have proper markup for handle', () => { 137 | const el = document.getElementById('slider1') 138 | const beerslider = new BeerSlider(el) 139 | expect(beerslider.handle.tagName).toEqual('SPAN') 140 | expect(beerslider.handle.getAttribute('class')).toContain('beer-handle') 141 | }) 142 | it('have proper markup for range slider if called with options', () => { 143 | const el = document.getElementById('slider1') 144 | const beerslider = new BeerSlider(el, {prefix: 'ba', start: 42}) 145 | expect(beerslider.handle.getAttribute('class')).toContain('ba-handle') 146 | }) 147 | it('should call the init method when images loaded', (done) => { 148 | spyOn(BeerSlider.prototype,'init') 149 | const el = document.getElementById('slider1') 150 | const beerslider = new BeerSlider(el) 151 | beerslider.loadedBoth().then(() => { 152 | expect(beerslider.init).toHaveBeenCalled() 153 | done() 154 | }) 155 | }) 156 | it('should call the init method when data-beer-src is found (slider does not use images)', (done) => { 157 | spyOn(BeerSlider.prototype,'init') 158 | const el = document.getElementById('slider4') 159 | const beerslider = new BeerSlider(el) 160 | beerslider.loadedBoth().then(() => { 161 | expect(beerslider.init).toHaveBeenCalled() 162 | done() 163 | }) 164 | }) 165 | it('should call the init method when no src is found (slider does not use images)', (done) => { 166 | spyOn(BeerSlider.prototype,'init') 167 | const el = document.getElementById('slider5') 168 | const beerslider = new BeerSlider(el) 169 | beerslider.loadedBoth().then(() => { 170 | expect(beerslider.init).toHaveBeenCalled() 171 | done() 172 | }) 173 | }) 174 | it('should not call the init method if images not loaded', (done) => { 175 | spyOn(BeerSlider.prototype,'init') 176 | spyOn(BeerSlider.prototype,'loadedBoth').and.returnValue(Promise.reject()) 177 | const el = document.getElementById('slider1') 178 | const beerslider = new BeerSlider(el) 179 | beerslider.loadedBoth().then(() => { 180 | }, 181 | () => { 182 | expect(beerslider.init).not.toHaveBeenCalled() 183 | done() 184 | }) 185 | }) 186 | it('should not call the init method if no element', () => { 187 | spyOn(BeerSlider.prototype,'init') 188 | const beerslider = new BeerSlider() 189 | expect(beerslider.init).not.toHaveBeenCalled() 190 | }) 191 | }) 192 | describe('', () => { 193 | it( '', () => { 194 | spyOn(BeerSlider.prototype,'loadedBoth') 195 | htmlToTest.innerHTML = innerHTML 196 | const el = document.getElementById('slider3') 197 | const beerslider = new BeerSlider(el) 198 | beerslider.onImagesLoad() 199 | expect(beerslider.revealElement).toBeFalsy() 200 | expect(beerslider.loadedBoth).not.toHaveBeenCalled() 201 | }) 202 | }) 203 | describe('the loadingImg method', () => { 204 | it('should return a promise', () => { 205 | const beerslider = new BeerSlider() 206 | expect(beerslider.loadingImg()).toEqual(jasmine.any(Promise)) 207 | }) 208 | }) 209 | describe('on init', () => { 210 | let el, beerslider 211 | beforeAll( () => { 212 | spyOn(BeerSlider.prototype,'setImgWidth') 213 | spyOn(BeerSlider.prototype,'move') 214 | spyOn(BeerSlider.prototype,'addListeners') 215 | spyOn(BeerSlider.prototype, 'loadingImg') 216 | }) 217 | beforeEach( () => { 218 | htmlToTest.innerHTML = innerHTML 219 | el = document.getElementById('slider1') 220 | beerslider = new BeerSlider(el) 221 | beerslider.init() 222 | }) 223 | afterEach( () => { 224 | htmlToTest.innerHTML = '' 225 | }) 226 | it('el should have beer-ready class', () => { 227 | expect(el.classList).toContain('beer-ready') 228 | }) 229 | it('the setImgWidth method should be called', () => { 230 | expect(beerslider.setImgWidth).toHaveBeenCalled() 231 | }) 232 | it('the move method should be called', () => { 233 | expect(beerslider.move).toHaveBeenCalled() 234 | }) 235 | it('addListeners method should be called', () => { 236 | expect(beerslider.addListeners).toHaveBeenCalled() 237 | }) 238 | }) 239 | describe('The move method', () => { 240 | let beerslider 241 | beforeEach( () => { 242 | htmlToTest.innerHTML = innerHTML 243 | const el = document.getElementById('slider1') 244 | beerslider = new BeerSlider(el) 245 | }) 246 | afterEach( () => { 247 | htmlToTest.innerHTML = '' 248 | }) 249 | it('should be defined', () => { 250 | expect(BeerSlider.prototype.move).toBeDefined() 251 | }) 252 | it('should modify the revealContainer style property width', () => { 253 | beerslider.range.value = 17 254 | beerslider.move() 255 | expect(beerslider.revealContainer.getAttribute('style')).toContain('width: 17%') 256 | }) 257 | it('should modify the handle style property left', () => { 258 | beerslider.range.value = 17 259 | beerslider.move() 260 | expect(beerslider.handle.getAttribute('style')).toContain('left: 17%') 261 | }) 262 | it('should modify the aria-valuenow attribure', () => { 263 | beerslider.range.value = 17 264 | beerslider.move() 265 | expect(beerslider.range.getAttribute('aria-valuenow')).toEqual('17') 266 | }) 267 | }) 268 | describe('The setImgWidth method', () => { 269 | beforeEach( () => { 270 | htmlToTest.innerHTML = innerHTML 271 | }) 272 | afterEach( () => { 273 | htmlToTest.innerHTML = '' 274 | }) 275 | it('should be defined', () => { 276 | expect(BeerSlider.prototype.setImgWidth).toBeDefined() 277 | }) 278 | it('should modify the revealElemet style property width', () => { 279 | const el = document.getElementById('slider1') 280 | const beerslider = new BeerSlider(el) 281 | beerslider.element.style.width = '340px' 282 | beerslider.setImgWidth() 283 | expect(beerslider.revealElement.getAttribute('style')).toContain('width: 340px') 284 | }) 285 | }) 286 | 287 | describe('the window resize', () => { 288 | beforeEach( () => { 289 | htmlToTest.innerHTML = innerHTML 290 | spyOn(BeerSlider.prototype, 'setImgWidth') 291 | }) 292 | afterEach( () => { 293 | htmlToTest.innerHTML = '' 294 | }) 295 | it('should call the setImgWidth method', () => { 296 | const el = document.getElementById('slider1') 297 | const beerslider = new BeerSlider(el) 298 | beerslider.addListeners() 299 | window.dispatchEvent(new Event('resize')) 300 | expect(beerslider.setImgWidth).toHaveBeenCalled() 301 | }) 302 | }) 303 | describe('the change event on range slider', () => { 304 | beforeEach( () => { 305 | htmlToTest.innerHTML = innerHTML 306 | spyOn(BeerSlider.prototype, 'move') 307 | }) 308 | afterEach( () => { 309 | htmlToTest.innerHTML = '' 310 | }) 311 | it('should call the move method', () => { 312 | const el = document.getElementById('slider1') 313 | const beerslider = new BeerSlider(el) 314 | beerslider.addListeners() 315 | beerslider.range.dispatchEvent(new Event('change')) 316 | expect(beerslider.move).toHaveBeenCalled() 317 | }) 318 | }) 319 | describe('the input event on range slider', () => { 320 | beforeEach( () => { 321 | htmlToTest.innerHTML = innerHTML 322 | spyOn(BeerSlider.prototype, 'move') 323 | }) 324 | afterEach( () => { 325 | htmlToTest.innerHTML = '' 326 | }) 327 | it('should call the move method', () => { 328 | const el = document.getElementById('slider1') 329 | const beerslider = new BeerSlider(el) 330 | beerslider.addListeners() 331 | beerslider.range.dispatchEvent(new Event('input')) 332 | expect(beerslider.move).toHaveBeenCalled() 333 | }) 334 | }) -------------------------------------------------------------------------------- /demo-assets/images/logos/Before_After.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/BeerSlider.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.BeerSlider=e():t.BeerSlider=e()}(window,function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=47)}([function(t,e){t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")()},function(t,e,n){var r=n(11)("wks"),o=n(33),i=n(0).Symbol,c=n(54);t.exports=function(t){return r[t]||(r[t]=c&&i[t]||(c?i:o)("Symbol."+t))}},function(t,e,n){var r=n(5);t.exports=function(t){if(!r(t))throw TypeError(String(t)+" is not an object!");return t}},function(t,e,n){var r=n(8),o=n(21);t.exports=n(6)?function(t,e,n){return r.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,e,n){t.exports=!n(12)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var r=n(6),o=n(31),i=n(2),c=n(32),u=Object.defineProperty;e.f=r?u:function(t,e,n){if(i(t),e=c(e,!0),i(n),o)try{return u(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){var r=n(0),o=n(3),i=n(14),c=n(19),u=n(57);t.exports=function(t,e){var n,a,s,f,l=t.target;if(n=t.global?r:t.stat?r[l]||c(l,{}):(r[l]||{}).prototype)for(a in e){if(s=n[a],f=e[a],!t.forced&&void 0!==s){if(typeof f==typeof s)continue;u(f,s)}(t.sham||s&&s.sham)&&o(f,"sham",!0),i(n,a,f,t.unsafe)}}},function(t,e){t.exports={}},function(t,e,n){var r=n(0),o=n(19),i=r["__core-js_shared__"]||o("__core-js_shared__",{});(t.exports=function(t,e){return i[t]||(i[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.0.0-beta.3",mode:n(13)?"pure":"global",copyright:"© 2018 Denis Pushkarev (zloirock.ru)"})},function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e){t.exports=!1},function(t,e,n){var r=n(0),o=n(3),i=n(4),c=n(19),u=n(34),a=n(15),s=a.get,f=a.enforce,l=String(u).split("toString");n(11)("inspectSource",function(t){return u.call(t)}),(t.exports=function(t,e,n,u){"function"==typeof n&&("string"!=typeof e||i(n,"name")||o(n,"name",e),f(n).source=l.join("string"==typeof e?e:"")),t===r?c(e,n):u?t[e]?t[e]=n:o(t,e,n):(delete t[e],o(t,e,n))})(Function.prototype,"toString",function(){return"function"==typeof this&&s(this).source||u.call(this)})},function(t,e,n){var r,o,i,c=n(0),u=n(34),a=n(5),s=n(3),f=n(4),l=n(22),p=n(23),v=c.WeakMap;if("function"==typeof v&&/native code/.test(u.call(v))){var d=new v,h=d.get,y=d.has,g=d.set;r=function(t,e){return g.call(d,t,e),e},o=function(t){return h.call(d,t)||{}},i=function(t){return y.call(d,t)}}else{var m=l("state");p[m]=!0,r=function(t,e){return s(t,m,e),e},o=function(t){return f(t,m)?t[m]:{}},i=function(t){return f(t,m)}}t.exports={set:r,get:o,has:i,enforce:function(t){return i(t)?o(t):r(t,{})},getterFor:function(t){return function(e){var n;if(!a(e)||(n=o(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required!");return n}}}},function(t,e,n){var r=n(60),o=n(25);t.exports=function(t){return r(o(t))}},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function!");return t}},function(t,e,n){"use strict";var r=n(17);t.exports.f=function(t){return new function(t){var e,n;this.promise=new t(function(t,r){if(void 0!==e||void 0!==n)throw TypeError("Bad Promise constructor");e=t,n=r}),this.resolve=r(e),this.reject=r(n)}(t)}},function(t,e,n){var r=n(0),o=n(3);t.exports=function(t,e){try{o(r,t,e)}catch(n){r[t]=e}return e}},function(t,e,n){var r=n(5),o=n(0).document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){var r=n(11)("keys"),o=n(33);t.exports=function(t){return r[t]||(r[t]=o(t))}},function(t,e){t.exports={}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},function(t,e){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(t,e,n){var r=n(8).f,o=n(4),i=n(1)("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},function(t,e){t.exports=function(t){try{return{e:!1,v:t()}}catch(t){return{e:!0,v:t}}}},function(t,e,n){t.exports=n(0)},function(t,e,n){var r=n(7),o=n(1)("toStringTag"),i="Arguments"==r(function(){return arguments}());t.exports=function(t){var e,n,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),o))?n:i?r(e):"Object"==(c=r(e))&&"function"==typeof e.callee?"Arguments":c}},function(t,e,n){t.exports=!n(6)&&!n(12)(function(){return 7!=Object.defineProperty(n(20)("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){var r=n(5);t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e,n){t.exports=n(11)("native-function-to-string",Function.toString)},function(t,e,n){"use strict";var r=n(9),o=n(66),i=n(39),c=n(71),u=n(27),a=n(3),s=n(14),f=n(13),l=n(1)("iterator"),p=n(10),v=n(38),d=v.IteratorPrototype,h=v.BUGGY_SAFARI_ITERATORS,y=function(){return this};t.exports=function(t,e,n,v,g,m,x){o(n,e,v);var b,j,S,w=function(t){if(t===g&&_)return _;if(!h&&t in E)return E[t];switch(t){case"keys":case"values":case"entries":return function(){return new n(this,t)}}return function(){return new n(this)}},O=e+" Iterator",P=!1,E=t.prototype,T=E[l]||E["@@iterator"]||g&&E[g],_=!h&&T||w(g),L="Array"==e&&E.entries||T;if(L&&(b=i(L.call(new t)),d!==Object.prototype&&b.next&&(f||i(b)===d||(c?c(b,d):"function"!=typeof b[l]&&a(b,l,y)),u(b,O,!0,!0),f&&(p[O]=y))),"values"==g&&T&&"values"!==T.name&&(P=!0,_=function(){return T.call(this)}),f&&!x||E[l]===_||a(E,l,_),p[e]=_,g)if(j={values:w("values"),keys:m?_:w("keys"),entries:w("entries")},x)for(S in j)!h&&!P&&S in E||s(E,S,j[S]);else r({target:e,proto:!0,forced:h||P},j);return j}},function(t,e,n){var r=n(4),o=n(16),i=n(61)(!1),c=n(23);t.exports=function(t,e){var n,u=o(t),a=0,s=[];for(n in u)!r(c,n)&&r(u,n)&&s.push(n);for(;e.length>a;)r(u,n=e[a++])&&(~i(s,n)||s.push(n));return s}},function(t,e,n){var r=n(24),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},function(t,e,n){"use strict";var r,o,i,c=n(39),u=n(3),a=n(4),s=n(13),f=n(1)("iterator"),l=!1;[].keys&&("next"in(i=[].keys())?(o=c(c(i)))!==Object.prototype&&(r=o):l=!0),void 0==r&&(r={}),s||a(r,f)||u(r,f,function(){return this}),t.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:l}},function(t,e,n){var r=n(4),o=n(67),i=n(22)("IE_PROTO"),c=n(68),u=Object.prototype;t.exports=c?Object.getPrototypeOf:function(t){return t=o(t),r(t,i)?t[i]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?u:null}},function(t,e,n){var r=n(2),o=n(69),i=n(26),c=n(41),u=n(20),a=n(22)("IE_PROTO"),s=function(){},f=function(){var t,e=u("iframe"),n=i.length;for(e.style.display="none",c.appendChild(e),e.src="javascript:",(t=e.contentWindow.document).open(),t.write("