rangetouch
Advanced tools
Comparing version 1.0.6 to 2.0.0-beta.1
@@ -9,4 +9,5 @@ { | ||
"esbenp.prettier-vscode", | ||
"shinnn.stylelint", | ||
"wayou.vscode-todo-highlight" | ||
] | ||
} |
# Changelog | ||
## v2.0.0 | ||
- Rewritten using ES6 syntax and a proper `RangeTouch` class | ||
- No longer using event delegation do to compatibility issues | ||
## v1.0.6 | ||
@@ -4,0 +9,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=t(e,document):"function"==typeof define&&define.amd?define(null,function(){t(e,document)}):e.rangetouch=t(e,document)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n={enabled:!0,addCSS:!0,thumbWidth:15,selectors:{range:'[type="range"]',disabled:".rangetouch--disabled"},events:{start:"touchstart",move:"touchmove",end:"touchend"}};function o(e,t,n){e.addEventListener(t,n,!1)}function a(e,t){if(t<1){var n=(o=(""+t).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/))?Math.max(0,(o[1]?o[1].length:0)-(o[2]?+o[2]:0)):0;return parseFloat(e.toFixed(n))}var o;return Math.round(e/t)*t}function d(o){var d;n.enabled&&"range"===o.target.type&&((d=o.target)instanceof HTMLElement&&!d.matches(n.selectors.disabled)&&!d.disabled)&&(o.preventDefault(),o.target.value=function(e){var t,o=e.target,d=e.changedTouches[0],i=parseFloat(o.getAttribute("min"))||0,r=parseFloat(o.getAttribute("max"))||100,u=parseFloat(o.getAttribute("step"))||1,s=r-i,c=o.getBoundingClientRect(),l=100/c.width*(n.thumbWidth/2)/100;return(t=100/c.width*(d.clientX-c.left))<0?t=0:t>100&&(t=100),t<50?t-=(100-2*t)*l:t>50&&(t+=2*(t-50)*l),i+a(s*(t/100),u)}(o),function(n,o,a){if(n&&o){var d;"function"==typeof e.CustomEvent?d=e.CustomEvent:(d=function(e,n){n=n||{bubbles:!1,cancelable:!1,detail:void 0};var o=t.createEvent("CustomEvent");return o.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),o}).prototype=e.Event.prototype;var i=new d(o,{bubbles:!0,detail:a});n.dispatchEvent(i)}}(o.target,o.type===n.events.end?"change":"input"))}return function(){if("ontouchstart"in t.documentElement){var e;n.addCSS&&((e=t.createElement("style")).appendChild(t.createTextNode("")),t.head.appendChild(e),e.sheet).insertRule([n.selectors.range,":not(",n.selectors.disabled,")"].join("")+" { user-select: none; -webkit-user-select: none; touch-action: manipulation; }",0),o(t.body,n.events.start,d),o(t.body,n.events.move,d),o(t.body,n.events.end,d)}}(),{set:function(e,t){n[e]=t}}}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define("RangeTouch",t):e.RangeTouch=t()}(this,function(){"use strict";function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}var t=function(e){return null!=e?e.constructor:null},n=function(e,t){return!!(e&&t&&e instanceof t)},r=function(e){return null==e},u=function(e){return t(e)===Object},i=function(e){return t(e)===String},o=function(e){return Array.isArray(e)},a=function(e){return n(e,NodeList)},c={nullOrUndefined:r,object:u,number:function(e){return t(e)===Number&&!Number.isNaN(e)},string:i,boolean:function(e){return t(e)===Boolean},function:function(e){return t(e)===Function},array:o,nodeList:a,element:function(e){return n(e,Element)},event:function(e){return n(e,Event)},empty:function(e){return r(e)||(i(e)||o(e)||a(e))&&!e.length||u(e)&&!Object.keys(e).length}};function l(e,t){if(1>t){var n=function(e){var t="".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);return t?Math.max(0,(t[1]?t[1].length:0)-(t[2]?+t[2]:0)):0}(t);return parseFloat(e.toFixed(n))}return Math.round(e/t)*t}var s={addCSS:!0,thumbWidth:15,watch:!0};return function(){function t(e,n){(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,t),c.element(e)?this.element=e:c.string(e)&&(this.element=document.querySelector(e)),c.element(this.element)&&c.empty(this.element.rangeTouch)&&(this.config=Object.assign({},s,n),this.init())}return n=t,u=[{key:"setup",value:function(e){var n=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},r=null;if(c.empty(e)||c.string(e)?r=Array.from(document.querySelectorAll(c.string(e)?e:'input[type="range"]')):c.element(e)?r=[e]:c.nodeList(e)?r=Array.from(e):c.array(e)&&(r=e.filter(c.element)),c.empty(r))return null;var u=Object.assign({},s,n);c.string(e)&&u.watch&&new MutationObserver(function(n){Array.from(n).forEach(function(n){Array.from(n.addedNodes).forEach(function(n){c.element(n)&&function(e,t){return function(){return Array.from(document.querySelectorAll(t)).includes(this)}.call(e,t)}(n,e)&&new t(n,u)})})}).observe(document.body,{childList:!0,subtree:!0});return r.map(function(e){return new t(e,n)})}},{key:"enabled",get:function(){return"ontouchstart"in document.documentElement}}],(r=[{key:"init",value:function(){t.enabled&&(this.config.addCSS&&(this.element.style.userSelect="none",this.element.style.webKitUserSelect="none",this.element.style.touchAction="manipulation"),this.listeners(!0),this.element.rangeTouch=this)}},{key:"destroy",value:function(){t.enabled&&(this.listeners(!1),this.element.rangeTouch=null)}},{key:"listeners",value:function(e){var t=this,n=e?"addEventListener":"removeEventListener";["touchstart","touchmove","touchend"].forEach(function(e){t.element[n](e,function(e){return t.set(e)},!1)})}},{key:"get",value:function(e){if(!t.enabled||!c.event(e))return null;var n,r=e.target,u=e.changedTouches[0],i=parseFloat(r.getAttribute("min"))||0,o=parseFloat(r.getAttribute("max"))||100,a=parseFloat(r.getAttribute("step"))||1,s=r.getBoundingClientRect(),f=100/s.width*(this.config.thumbWidth/2)/100;return 0>(n=100/s.width*(u.clientX-s.left))?n=0:100<n&&(n=100),50>n?n-=(100-2*n)*f:50<n&&(n+=2*(n-50)*f),i+l(n/100*(o-i),a)}},{key:"set",value:function(e){t.enabled&&c.event(e)&&!e.target.disabled&&(e.preventDefault(),e.target.value=this.get(e),function(e,t){if(e&&t){var n=new Event(t);e.dispatchEvent(n)}}(e.target,"touchend"===e.type?"change":"input"))}}])&&e(n.prototype,r),u&&e(n,u),t;var n,r,u}()}); |
@@ -1,1 +0,1 @@ | ||
!function(){"use strict";function e(e){var n=new XMLHttpRequest,t=document.body;if("withCredentials"in n)n.open("GET",e,!0);else{if("function"!=typeof XDomainRequest)return;(n=new XDomainRequest).open("GET",e)}n.onload=function(){var e=document.createElement("div");e.setAttribute("hidden",""),e.innerHTML=n.responseText,t.insertBefore(e,t.childNodes[0])},setTimeout(function(){n.send()},0)}var n,t,o,a,i,c;window.loadSprites=function(n){n.forEach(e)},shr.setup({count:{classname:"btn__count"}}),window.rangetouch.set("thumbWidth",20),document.domain.indexOf("rangetouch.com")>-1&&(n=window,t=document,o="script",a="ga",n.GoogleAnalyticsObject=a,n.ga=n.ga||function(){(n.ga.q=n.ga.q||[]).push(arguments)},n.ga.l=1*new Date,i=t.createElement(o),c=t.getElementsByTagName(o)[0],i.async=1,i.src="//www.google-analytics.com/analytics.js",c.parentNode.insertBefore(i,c),ga("create","UA-40881672-15","auto"),ga("send","pageview"))}(); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t():"function"==typeof define&&define.amd?define("RangeTouch",t):t()}(0,function(){"use strict";function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}var t=function(e){return null!=e?e.constructor:null},n=function(e,t){return!!(e&&t&&e instanceof t)},r=function(e){return null==e},i=function(e){return t(e)===Object},u=function(e){return t(e)===String},o=function(e){return Array.isArray(e)},c=function(e){return n(e,NodeList)},a={nullOrUndefined:r,object:i,number:function(e){return t(e)===Number&&!Number.isNaN(e)},string:u,boolean:function(e){return t(e)===Boolean},function:function(e){return t(e)===Function},array:o,nodeList:c,element:function(e){return n(e,Element)},event:function(e){return n(e,Event)},empty:function(e){return r(e)||(u(e)||o(e)||c(e))&&!e.length||i(e)&&!Object.keys(e).length}};function l(e,t){if(1>t){var n=function(e){var t="".concat(e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);return t?Math.max(0,(t[1]?t[1].length:0)-(t[2]?+t[2]:0)):0}(t);return parseFloat(e.toFixed(n))}return Math.round(e/t)*t}var s={addCSS:!0,thumbWidth:15,watch:!0},f=function(){function t(e,n){(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,t),a.element(e)?this.element=e:a.string(e)&&(this.element=document.querySelector(e)),a.element(this.element)&&a.empty(this.element.rangeTouch)&&(this.config=Object.assign({},s,n),this.init())}return n=t,i=[{key:"setup",value:function(e){var n=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},r=null;if(a.empty(e)||a.string(e)?r=Array.from(document.querySelectorAll(a.string(e)?e:'input[type="range"]')):a.element(e)?r=[e]:a.nodeList(e)?r=Array.from(e):a.array(e)&&(r=e.filter(a.element)),a.empty(r))return null;var i=Object.assign({},s,n);a.string(e)&&i.watch&&new MutationObserver(function(n){Array.from(n).forEach(function(n){Array.from(n.addedNodes).forEach(function(n){a.element(n)&&function(e,t){return function(){return Array.from(document.querySelectorAll(t)).includes(this)}.call(e,t)}(n,e)&&new t(n,i)})})}).observe(document.body,{childList:!0,subtree:!0});return r.map(function(e){return new t(e,n)})}},{key:"enabled",get:function(){return"ontouchstart"in document.documentElement}}],(r=[{key:"init",value:function(){t.enabled&&(this.config.addCSS&&(this.element.style.userSelect="none",this.element.style.webKitUserSelect="none",this.element.style.touchAction="manipulation"),this.listeners(!0),this.element.rangeTouch=this)}},{key:"destroy",value:function(){t.enabled&&(this.listeners(!1),this.element.rangeTouch=null)}},{key:"listeners",value:function(e){var t=this,n=e?"addEventListener":"removeEventListener";["touchstart","touchmove","touchend"].forEach(function(e){t.element[n](e,function(e){return t.set(e)},!1)})}},{key:"get",value:function(e){if(!t.enabled||!a.event(e))return null;var n,r=e.target,i=e.changedTouches[0],u=parseFloat(r.getAttribute("min"))||0,o=parseFloat(r.getAttribute("max"))||100,c=parseFloat(r.getAttribute("step"))||1,s=r.getBoundingClientRect(),f=100/s.width*(this.config.thumbWidth/2)/100;return 0>(n=100/s.width*(i.clientX-s.left))?n=0:100<n&&(n=100),50>n?n-=(100-2*n)*f:50<n&&(n+=2*(n-50)*f),u+l(n/100*(o-u),c)}},{key:"set",value:function(e){t.enabled&&a.event(e)&&!e.target.disabled&&(e.preventDefault(),e.target.value=this.get(e),function(e,t){if(e&&t){var n=new Event(t);e.dispatchEvent(n)}}(e.target,"touchend"===e.type?"change":"input"))}}])&&e(n.prototype,r),i&&e(n,i),t;var n,r,i}();document.addEventListener("DOMContentLoaded",function(){["dist/docs.svg"].forEach(function(e){var t=new XMLHttpRequest,n=document.body;"withCredentials"in t&&(t.open("GET",e,!0),t.onload=function(){var e=document.createElement("div");e.setAttribute("hidden",""),e.innerHTML=t.responseText,n.insertBefore(e,n.childNodes[0])},t.send())}),window.shr.setup({count:{classname:"btn__count"}}),f.setup(".js-example",{thumbWidth:20})})}); |
@@ -5,27 +5,21 @@ // ========================================================================== | ||
/*global shr*/ | ||
import RangeTouch from '../../../src/js/rangetouch'; | ||
(function() { | ||
'use strict'; | ||
document.addEventListener('DOMContentLoaded', () => { | ||
const loadSprite = url => { | ||
const xhr = new XMLHttpRequest(); | ||
const { body } = document; | ||
function loadSprite(url) { | ||
var xhr = new XMLHttpRequest(), | ||
body = document.body; | ||
// Check for CORS support | ||
// If you're loading from same domain, you can remove the whole if/else statement | ||
// XHR for Chrome/Firefox/Opera/Safari/IE10+ | ||
if ('withCredentials' in xhr) { | ||
xhr.open('GET', url, true); | ||
if (!('withCredentials' in xhr)) { | ||
return; | ||
} | ||
// XDomainRequest for IE8 & IE9 | ||
else if (typeof XDomainRequest == 'function') { | ||
xhr = new XDomainRequest(); | ||
xhr.open('GET', url); | ||
} | ||
else { return; } | ||
xhr.open('GET', url, true); | ||
// Inject hidden div with sprite on load | ||
xhr.onload = function() { | ||
var container = document.createElement('div'); | ||
xhr.onload = () => { | ||
const container = document.createElement('div'); | ||
container.setAttribute('hidden', ''); | ||
@@ -36,32 +30,24 @@ container.innerHTML = xhr.responseText; | ||
// Timeout for IE9 | ||
setTimeout(function () { | ||
xhr.send(); | ||
}, 0); | ||
} | ||
window.loadSprites = function(sprites) { | ||
sprites.forEach(loadSprite); | ||
}; | ||
xhr.send(); | ||
}; | ||
['dist/docs.svg'].forEach(loadSprite); | ||
// Setup shr | ||
shr.setup({ | ||
count: { | ||
classname: 'btn__count' | ||
} | ||
}); | ||
window.shr.setup({ | ||
count: { | ||
classname: 'btn__count', | ||
}, | ||
}); | ||
// Set range thumb size | ||
window.rangetouch.set('thumbWidth', 20); | ||
RangeTouch.setup('.js-example', { thumbWidth: 20 }); | ||
// Google analytics | ||
// For demo site (https://rangetouch.com) only | ||
if(document.domain.indexOf('rangetouch.com') > -1) { | ||
(function(i,s,o,g,r,a,m){i.GoogleAnalyticsObject=r;i[r]=i[r]||function(){ | ||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | ||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | ||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | ||
ga('create', 'UA-40881672-15', 'auto'); | ||
ga('send', 'pageview'); | ||
} | ||
})(); | ||
// Test MutationObserver | ||
/* setTimeout(() => { | ||
const input = document.createElement('input'); | ||
input.type = 'range'; | ||
input.className = 'js-example'; | ||
document.getElementById('test').appendChild(input); | ||
}, 2000); */ | ||
}); |
125
gulpfile.js
@@ -7,17 +7,32 @@ // ========================================================================== | ||
const gulp = require('gulp'); | ||
const plumber = require('gulp-plumber'); | ||
const concat = require('gulp-concat'); | ||
const terser = require('gulp-terser'); | ||
// CSS | ||
const less = require('gulp-less'); | ||
const clean = require('gulp-clean-css'); | ||
const prefix = require('gulp-autoprefixer'); | ||
// JavaScript | ||
const terser = require('gulp-terser'); | ||
const rollup = require('gulp-better-rollup'); | ||
const babel = require('rollup-plugin-babel'); | ||
const commonjs = require('rollup-plugin-commonjs'); | ||
const resolve = require('rollup-plugin-node-resolve'); | ||
const sourcemaps = require('gulp-sourcemaps'); | ||
// SVGs | ||
const svgstore = require('gulp-svgstore'); | ||
const svgmin = require('gulp-svgmin'); | ||
// Utils | ||
const plumber = require('gulp-plumber'); | ||
const concat = require('gulp-concat'); | ||
const rename = require('gulp-rename'); | ||
const replace = require('gulp-replace'); | ||
const open = require('gulp-open'); | ||
const size = require('gulp-size'); | ||
const log = require('fancy-log'); | ||
// Deployment | ||
const aws = require('aws-sdk'); | ||
const publish = require('gulp-awspublish'); | ||
const log = require('fancy-log'); | ||
const open = require('gulp-open'); | ||
@@ -76,21 +91,68 @@ const pkg = require('./package.json'); | ||
// Babel config | ||
const babelrc = { | ||
babelrc: false, | ||
presets: [ | ||
'@babel/env', | ||
[ | ||
'minify', | ||
{ | ||
builtIns: false, // Temporary fix for https://github.com/babel/minify/issues/904 | ||
}, | ||
], | ||
], | ||
}; | ||
// Size plugin | ||
const sizeOptions = { showFiles: true, gzip: true }; | ||
// JavaScript | ||
// Formats to build | ||
const formats = { | ||
es: { | ||
ext: 'mjs', | ||
polyfill: false, | ||
}, | ||
umd: { | ||
ext: 'js', | ||
polyfill: false, | ||
}, | ||
}; | ||
const namespace = 'RangeTouch'; | ||
const build = { | ||
js: (files, bundle) => { | ||
Object.keys(files).forEach(key => { | ||
const name = `js-${key}`; | ||
tasks.js.push(name); | ||
Object.entries(formats).forEach(([format, task]) => { | ||
Object.keys(files).forEach(key => { | ||
const name = `js-${key}`; | ||
tasks.js.push(name); | ||
gulp.task(name, () => { | ||
return gulp | ||
.src(bundles[bundle].js[key]) | ||
.pipe(plumber()) | ||
.pipe(concat(key)) | ||
.pipe(terser()) | ||
.pipe( | ||
size({ | ||
showFiles: true, | ||
gzip: true, | ||
}), | ||
) | ||
.pipe(gulp.dest(paths[bundle].output)); | ||
gulp.task(name, () => { | ||
return gulp | ||
.src(bundles[bundle].js[key]) | ||
.pipe(plumber()) | ||
.pipe(concat(key)) | ||
.pipe(sourcemaps.init()) | ||
.pipe( | ||
rollup( | ||
{ | ||
plugins: [resolve(), commonjs(), babel(babelrc)], | ||
}, | ||
{ | ||
name: namespace, | ||
// exports: 'named', | ||
format, | ||
}, | ||
), | ||
) | ||
.pipe(terser()) | ||
.pipe( | ||
rename({ | ||
extname: `.${task.ext}`, | ||
}), | ||
) | ||
.pipe(size(sizeOptions)) | ||
.pipe(gulp.dest(paths[bundle].output)); | ||
}); | ||
}); | ||
@@ -115,8 +177,3 @@ }); | ||
.pipe(clean()) | ||
.pipe( | ||
size({ | ||
showFiles: true, | ||
gzip: true, | ||
}), | ||
) | ||
.pipe(size(sizeOptions)) | ||
.pipe(gulp.dest(paths[bundle].output)); | ||
@@ -149,8 +206,3 @@ }); | ||
) | ||
.pipe( | ||
size({ | ||
showFiles: true, | ||
gzip: true, | ||
}), | ||
) | ||
.pipe(size(sizeOptions)) | ||
.pipe(gulp.dest(paths[bundle].output)); | ||
@@ -200,5 +252,6 @@ }); | ||
const regex = '(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)'; | ||
const regex = | ||
'(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*).(?:0|[1-9][0-9]*)(?:-[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:.[\\da-z\\-]+)*)?'; | ||
const semver = new RegExp(`v${regex}`, 'gi'); | ||
const cdnpath = new RegExp(`${deploy.cdn.domain}/${regex}`, 'gi'); | ||
const semver = new RegExp(`v${regex}`, 'gi'); | ||
const localpath = new RegExp('(../)?dist', 'gi'); | ||
@@ -303,2 +356,2 @@ | ||
// Do everything | ||
gulp.task('publish', gulp.series(gulp.parallel(...tasks.js, ...tasks.less, ...tasks.sprite), 'cdn', 'docs')); | ||
gulp.task('deploy', gulp.series(gulp.parallel(...tasks.js, ...tasks.less, ...tasks.sprite), 'cdn', 'docs')); |
{ | ||
"name": "rangetouch", | ||
"version": "1.0.6", | ||
"version": "2.0.0-beta.1", | ||
"description": "A super tiny library to make input type='range' sliders work better on touch devices", | ||
"homepage": "https://rangetouch.com", | ||
"author": "Sam Potts <sam@potts.es>", | ||
"license": "MIT", | ||
"main": "src/js/rangetouch.js", | ||
@@ -19,3 +21,2 @@ "browserslist": [ | ||
}, | ||
"license": "MIT", | ||
"bugs": { | ||
@@ -30,6 +31,9 @@ "url": "https://github.com/sampotts/rangetouch/issues" | ||
}, | ||
"author": "Sam Potts <sam@potts.es>", | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"aws-sdk": "^2.400.0", | ||
"@babel/core": "^7.2.2", | ||
"@babel/preset-env": "^7.3.1", | ||
"babel-eslint": "^10.0.1", | ||
"babel-preset-minify": "^0.5.0", | ||
"eslint": "^5.13.0", | ||
@@ -43,2 +47,3 @@ "eslint-config-airbnb-base": "^13.1.0", | ||
"gulp-awspublish": "^4.0.0", | ||
"gulp-better-rollup": "^3.4.0", | ||
"gulp-clean-css": "^4.0.0", | ||
@@ -51,8 +56,21 @@ "gulp-concat": "^2.6.1", | ||
"gulp-replace": "^1.0.0", | ||
"gulp-s3": "^0.11.0", | ||
"gulp-size": "^3.0.0", | ||
"gulp-sourcemaps": "^2.6.4", | ||
"gulp-svgmin": "^2.1.0", | ||
"gulp-svgstore": "^7.0.1", | ||
"gulp-terser": "^1.1.7", | ||
"gulp-util": "^3.0.8" | ||
"prettier-eslint": "^8.8.2", | ||
"prettier-stylelint": "^0.4.2", | ||
"rollup-plugin-babel": "^4.0.3", | ||
"rollup-plugin-commonjs": "^9.2.0", | ||
"rollup-plugin-node-resolve": "^3.4.0", | ||
"rollup-plugin-replace": "^2.1.0", | ||
"rollup-plugin-uglify": "^6.0.0", | ||
"run-sequence": "^2.2.1", | ||
"stylelint": "^9.7.1", | ||
"stylelint-config-prettier": "^4.0.0", | ||
"stylelint-config-recommended": "^2.1.0", | ||
"stylelint-order": "^1.0.0" | ||
} | ||
} |
@@ -5,8 +5,6 @@ # RangeTouch | ||
[Donate to support RangeTouch](#donate) | ||
[Donate](#donate) - [Demo](https://rangetouch.com) - [![npm version](https://badge.fury.io/js/rangetouch.svg)](https://badge.fury.io/js/rangetouch) | ||
[Checkout the demo](https://rangetouch.com) | ||
## Why bother? | ||
## Why? | ||
While building [plyr](https://plyr.io) I noticed how bad the experience was trying to use `<input type="range">` is on a touch device (particularly iOS). Touching the track on a desktop will jump the thumb handle to that point. However on some touch devices this simply focuses the input and to adjust the value you need to touch and drag the handle. This is something that I can't help but feel will eventually be fixed by the browser vendors but for now, you can use RangeTouch to fill that gap. | ||
@@ -16,20 +14,18 @@ | ||
- No setup required, just include the script | ||
- Less than 1KB minified and gzipped | ||
- No dependencies (written in "vanilla" JavaScript) | ||
- Uses event delgation so no need to re-run after DOM manipulation | ||
## Quick setup | ||
To use RangeTouch, you just need to add `rangetouch.js` (either from the `/dist` (minified) or `/src/js` (unminified) folders). Ideally before the closing `</body>` tag: | ||
### 1. Include the lib | ||
```html | ||
<script src="/path/to/rangetouch.js" async></script> | ||
Either use the ES6 module: | ||
```javascript | ||
import RangeTouch from 'rangetouch'; | ||
``` | ||
It will automatically bind to all `<input type="range">` elements, even newly injected ones as it uses event delegation. | ||
...or include the script: | ||
### CDN | ||
You can load RangeTouch from our CDN (backed by the awesome [Fastly](https://www.fastly.com/)) if you'd like: | ||
```html | ||
@@ -39,39 +35,62 @@ <script src="https://rangetouch.com/1.0.6/rangetouch.js"></script> | ||
### Node / NPM | ||
### 2. Create instance(s) | ||
[![npm version](https://badge.fury.io/js/rangetouch.svg)](https://badge.fury.io/js/rangetouch) | ||
#### Single instance | ||
```bash | ||
npm install rangetouch | ||
```javascript | ||
const range = new RangeTouch('input[type="range"]', { ...options }); | ||
``` | ||
[https://www.npmjs.com/package/rangetouch](https://www.npmjs.com/package/rangetouch) | ||
The first argument can either be: | ||
### Bower | ||
- a [valid CSS selector](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) | ||
- an [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) | ||
[![Bower version](https://badge.fury.io/bo/rangetouch.svg)](https://badge.fury.io/bo/rangetouch) | ||
The second argument is for options. | ||
```bash | ||
bower install rangetouch | ||
This will return a reference to the single instance. | ||
#### Multiple instances | ||
To setup multiple inputs at one time, you can use the following static method: | ||
```javascript | ||
const ranges = RangeTouch.setup('input[type="range"]', { ...options }); | ||
``` | ||
[http://bower.io/search/?q=rangetouch](http://bower.io/search/?q=rangetouch) | ||
The first argument can either be: | ||
More info on setting up dependencies can be found in the [Bower Docs](http://bower.io/docs/creating-packages/#maintaining-dependencies) | ||
- a [valid CSS selector](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) | ||
- an [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) | ||
- an [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList) | ||
- an [Element](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) of [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) | ||
## Configuration | ||
The second argument is for options. | ||
If you're customizing your range inputs (easily done - see the demo for an example) and you change the size of the thumb handle, you should specify (in pixels) this after including the script: | ||
This will return an array of RangeTouch instances that it setup. | ||
## Options | ||
| Property | Type | Default | Description | | ||
| ---------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ||
| addCSS | Boolean | `true` | Whether to inject CSS to improve the usability of the inputs. It's recommended you add this yourself if you don't want RangeTouch to take care of it. | | ||
| thumbWidth | Integer | `15` | This value is used as part of the calculation to determine the value the users selects when touching the range track. Unfortunately as JavaScript can't access the shadow DOM, this value can't be automatically determined. I would recommend customisation (and setting the size of the thumb) given all OS and browser combinations seem to render the control differently. | | ||
| watch | Boolean | `true` | Watch for new elements added to the DOM that match your string selector. **Note**: This only applies when using the multiple instance `RangeTouch` setup method and also requires a string selector as the first argument. | | ||
## API | ||
| Method | Arguments | Description | | ||
| --------- | --------- | -------------------------------------------------------- | | ||
| destroy() | N/A | Destroy the current instance and remove event listeners. | | ||
To call an API method, you need a reference to the instance. For example: | ||
```javascript | ||
window.rangetouch.set('thumbWidth', 15); | ||
const range = new RangeTouch('input[type="range"]', { ...options }); | ||
range.destroy(); | ||
``` | ||
This value is used as part of the calculation to determine the value the users selects when touching the range track. Unfortunately as JavaScript can't access the shadow DOM, this value can't be automatically determined. I would recommend customisation (and setting the size of the thumb) given all OS and browser combinations seem to render the control differently (as per usual). | ||
If you want to disable RangeTouch for a particular input, add the `rangetouch--disabled` class name to the element. | ||
## Issues | ||
If you find anything weird with RangeTouch, please let us know using the GitHub issues tracker. | ||
If you find anything weird with RangeTouch, please let us know using the [GitHub issues tracker](https://github.com/sampotts/rangetouch/issues) and be descriptive on how to reproduce, expected result, the browser (and version) used, etc. | ||
@@ -84,3 +103,3 @@ ## Author | ||
RangeTouch costs money to run, not my time - I donate that for free but domains, hosting and more. Any help is appreciated... | ||
RangeTouch costs money to run for domains, hosting and more. Any help is appreciated... | ||
[Donate to support RangeTouch](https://www.paypal.me/pottsy/20usd) | ||
@@ -90,3 +109,3 @@ | ||
[![Fastly](https://www.fastly.com/sites/all/themes/custom/fastly2016/logo.png)](https://www.fastly.com/) | ||
[![Fastly](https://cdn.plyr.io/static/fastly-logo.png)](https://www.fastly.com/) | ||
@@ -93,0 +112,0 @@ Thanks to [Fastly](https://www.fastly.com/) for providing the CDN services. |
// ========================================================================== | ||
// rangetouch.js v1.0.6 | ||
// rangetouch.js v2.0.0-beta.1 | ||
// Making <input type="range"> work on touch devices | ||
// https://github.com/selz/rangetouch | ||
// https://github.com/sampotts/rangetouch | ||
// License: The MIT License (MIT) | ||
// ========================================================================== | ||
(function(root, factory) { | ||
'use strict'; | ||
/*global define,module*/ | ||
import { matches } from './utils/css'; | ||
import { trigger } from './utils/events'; | ||
import is from './utils/is'; | ||
import { round } from './utils/numbers'; | ||
if (typeof module === 'object' && typeof module.exports === 'object') { | ||
// Node, CommonJS-like | ||
module.exports = factory(root, document); | ||
} else if (typeof define === 'function' && define.amd) { | ||
// AMD | ||
define(null, function() { | ||
factory(root, document); | ||
}); | ||
} else { | ||
// Browser globals (root is window) | ||
root.rangetouch = factory(root, document); | ||
} | ||
})(typeof window !== 'undefined' ? window : this, function(window, document) { | ||
'use strict'; | ||
const defaults = { | ||
addCSS: true, // Add CSS to the element to improve usability (required here or in your CSS!) | ||
thumbWidth: 15, // The width of the thumb handle | ||
watch: true, // Watch for new elements that match a string target | ||
}; | ||
// Default config | ||
var settings = { | ||
enabled: true, | ||
addCSS: true, | ||
thumbWidth: 15, | ||
selectors: { | ||
range: '[type="range"]', | ||
disabled: '.rangetouch--disabled', | ||
}, | ||
events: { | ||
start: 'touchstart', | ||
move: 'touchmove', | ||
end: 'touchend', | ||
}, | ||
}; | ||
class RangeTouch { | ||
/** | ||
* Setup a new instance | ||
* @param {String|Element} target | ||
* @param {Object} options | ||
*/ | ||
constructor(target, options) { | ||
if (is.element(target)) { | ||
// An Element is passed, use it directly | ||
this.element = target; | ||
} else if (is.string(target)) { | ||
// A CSS Selector is passed, fetch it from the DOM | ||
this.element = document.querySelector(target); | ||
} | ||
// Setup | ||
function setup() { | ||
// Bail if not a touch enabled device | ||
if (!('ontouchstart' in document.documentElement)) { | ||
if (!is.element(this.element) || !is.empty(this.element.rangeTouch)) { | ||
return; | ||
} | ||
// Add useful CSS | ||
if (settings.addCSS) { | ||
var stylesheet = createStyleSheet(); | ||
stylesheet.insertRule( | ||
getSelector() + ' { user-select: none; -webkit-user-select: none; touch-action: manipulation; }', | ||
0, | ||
); | ||
} | ||
this.config = Object.assign({}, defaults, options); | ||
// Listen for events | ||
listeners(); | ||
this.init(); | ||
} | ||
// Event listeners | ||
function listeners() { | ||
on(document.body, settings.events.start, set); | ||
on(document.body, settings.events.move, set); | ||
on(document.body, settings.events.end, set); | ||
static get enabled() { | ||
return 'ontouchstart' in document.documentElement; | ||
} | ||
// Create a CSS stylesheet | ||
function createStyleSheet() { | ||
var style = document.createElement('style'); | ||
style.appendChild(document.createTextNode('')); | ||
document.head.appendChild(style); | ||
return style.sheet; | ||
} | ||
/** | ||
* Setup multiple instances | ||
* @param {String|Element|NodeList|Array} target | ||
* @param {Object} options | ||
*/ | ||
static setup(target, options = {}) { | ||
let targets = null; | ||
// Trigger event | ||
function trigger(element, type, properties) { | ||
// Bail if no element | ||
if (!element || !type) { | ||
return; | ||
if (is.empty(target) || is.string(target)) { | ||
targets = Array.from(document.querySelectorAll(is.string(target) ? target : 'input[type="range"]')); | ||
} else if (is.element(target)) { | ||
targets = [target]; | ||
} else if (is.nodeList(target)) { | ||
targets = Array.from(target); | ||
} else if (is.array(target)) { | ||
targets = target.filter(is.element); | ||
} | ||
// Create CustomEvent constructor | ||
var CustomEvent; | ||
if (typeof window.CustomEvent === 'function') { | ||
CustomEvent = window.CustomEvent; | ||
} else { | ||
// Polyfill CustomEvent | ||
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill | ||
CustomEvent = function(event, params) { | ||
params = params || { | ||
bubbles: false, | ||
cancelable: false, | ||
detail: undefined, | ||
}; | ||
var custom = document.createEvent('CustomEvent'); | ||
custom.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); | ||
return custom; | ||
}; | ||
CustomEvent.prototype = window.Event.prototype; | ||
if (is.empty(targets)) { | ||
return null; | ||
} | ||
// Create and dispatch the event | ||
var event = new CustomEvent(type, { | ||
bubbles: true, | ||
detail: properties, | ||
}); | ||
const config = Object.assign({}, defaults, options); | ||
// Dispatch the event | ||
element.dispatchEvent(event); | ||
} | ||
if (is.string(target) && config.watch) { | ||
// Create an observer instance | ||
const observer = new MutationObserver(mutations => { | ||
Array.from(mutations).forEach(mutation => { | ||
Array.from(mutation.addedNodes).forEach(node => { | ||
if (!is.element(node) || !matches(node, target)) { | ||
return; | ||
} | ||
// Get the selector for the range | ||
function getSelector() { | ||
return [settings.selectors.range, ':not(', settings.selectors.disabled, ')'].join(''); | ||
} | ||
// eslint-disable-next-line no-unused-vars | ||
const range = new RangeTouch(node, config); | ||
}); | ||
}); | ||
}); | ||
// Check if element is disabled | ||
function isDisabled(element) { | ||
if (element instanceof HTMLElement) { | ||
return element.matches(settings.selectors.disabled) || element.disabled; | ||
// Pass in the target node, as well as the observer options | ||
observer.observe(document.body, { | ||
childList: true, | ||
subtree: true, | ||
}); | ||
} | ||
return true; | ||
return targets.map(t => new RangeTouch(t, options)); | ||
} | ||
// Bind an event listener | ||
function on(element, type, listener) { | ||
element.addEventListener(type, listener, false); | ||
} | ||
init() { | ||
// Bail if not a touch enabled device | ||
if (!RangeTouch.enabled) { | ||
return; | ||
} | ||
// Get the number of decimal places | ||
function getDecimalPlaces(value) { | ||
var match = ('' + value).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); | ||
if (!match) { | ||
return 0; | ||
// Add useful CSS | ||
if (this.config.addCSS) { | ||
// TODO: Restore original values on destroy | ||
this.element.style.userSelect = 'none'; | ||
this.element.style.webKitUserSelect = 'none'; | ||
this.element.style.touchAction = 'manipulation'; | ||
} | ||
return Math.max( | ||
0, | ||
// Number of digits right of decimal point. | ||
(match[1] ? match[1].length : 0) - | ||
// Adjust for scientific notation. | ||
(match[2] ? +match[2] : 0), | ||
); | ||
this.listeners(true); | ||
this.element.rangeTouch = this; | ||
} | ||
// Round to the nearest step | ||
function round(number, step) { | ||
if (step < 1) { | ||
var places = getDecimalPlaces(step); | ||
return parseFloat(number.toFixed(places)); | ||
destroy() { | ||
// Bail if not a touch enabled device | ||
if (!RangeTouch.enabled) { | ||
return; | ||
} | ||
return Math.round(number / step) * step; | ||
this.listeners(false); | ||
this.element.rangeTouch = null; | ||
} | ||
// Get the value based on touch position | ||
function get(event) { | ||
var input = event.target; | ||
var touch = event.changedTouches[0]; | ||
var min = parseFloat(input.getAttribute('min')) || 0; | ||
var max = parseFloat(input.getAttribute('max')) || 100; | ||
var step = parseFloat(input.getAttribute('step')) || 1; | ||
var delta = max - min; | ||
listeners(toggle) { | ||
const method = toggle ? 'addEventListener' : 'removeEventListener'; | ||
// Listen for events | ||
['touchstart', 'touchmove', 'touchend'].forEach(type => { | ||
this.element[method](type, event => this.set(event), false); | ||
}); | ||
} | ||
/** | ||
* Get the value based on touch position | ||
* @param {Event} event | ||
*/ | ||
get(event) { | ||
if (!RangeTouch.enabled || !is.event(event)) { | ||
return null; | ||
} | ||
const input = event.target; | ||
const touch = event.changedTouches[0]; | ||
const min = parseFloat(input.getAttribute('min')) || 0; | ||
const max = parseFloat(input.getAttribute('max')) || 100; | ||
const step = parseFloat(input.getAttribute('step')) || 1; | ||
const delta = max - min; | ||
// Calculate percentage | ||
var percent; | ||
var clientRect = input.getBoundingClientRect(); | ||
var thumbWidth = ((100 / clientRect.width) * (settings.thumbWidth / 2)) / 100; | ||
let percent; | ||
const clientRect = input.getBoundingClientRect(); | ||
const thumbWidth = ((100 / clientRect.width) * (this.config.thumbWidth / 2)) / 100; | ||
@@ -195,6 +177,8 @@ // Determine left percentage | ||
// Update range value based on position | ||
function set(event) { | ||
// If not enabled, bail | ||
if (!settings.enabled || event.target.type !== 'range' || isDisabled(event.target)) { | ||
/** | ||
* Update range value based on position | ||
* @param {Event} event | ||
*/ | ||
set(event) { | ||
if (!RangeTouch.enabled || !is.event(event) || event.target.disabled) { | ||
return; | ||
@@ -207,16 +191,9 @@ } | ||
// Set value | ||
event.target.value = get(event); | ||
event.target.value = this.get(event); | ||
// Trigger input event | ||
trigger(event.target, event.type === settings.events.end ? 'change' : 'input'); | ||
// Trigger event | ||
trigger(event.target, event.type === 'touchend' ? 'change' : 'input'); | ||
} | ||
} | ||
// Run setup automatically | ||
setup(); | ||
return { | ||
set: function(setting, value) { | ||
settings[setting] = value; | ||
}, | ||
}; | ||
}); | ||
export default RangeTouch; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
470141
49
112
39
674
2
1