├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── build.js ├── demos ├── create.html ├── default.html ├── dom.html └── events.html ├── dist ├── basicLightbox.min.css └── basicLightbox.min.js ├── package.json └── src ├── scripts └── main.js └── styles └── main.scss /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/**/* binary -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: electerious 2 | custom: ['https://paypal.me/electerious', 'https://www.buymeacoffee.com/electerious'] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | yarn.lock 3 | package-lock.json -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [5.0.4] - 2021-01-17 8 | 9 | ### Changed 10 | 11 | - Updated dependencies 12 | 13 | ## [5.0.3] - 2020-03-20 14 | 15 | ### Changed 16 | 17 | - Updated dependencies 18 | 19 | ## [5.0.2] - 2019-02-23 20 | 21 | ### Changed 22 | 23 | - Replace `gulp` and `basicTasks` with custom build process 24 | 25 | ## [5.0.1] - 2018-09-23 26 | 27 | ### Added 28 | 29 | - Link to CodePen demos 30 | 31 | ## [5.0.0] - 2018-09-23 32 | 33 | ### Added 34 | 35 | - Node elements and templates in `.create()` (#15, #17) 36 | - `onShow` and `onClose` callbacks 37 | - Streamlined API with less options to make basicLightbox easier to use 38 | - More demos 39 | 40 | ### Changed 41 | 42 | - Removed `beforeShow` and `afterShow` callbacks 43 | - Removed `beforeClose` and `afterClose` callbacks 44 | - Removed `beforePlaceholder` and `afterPlaceholder` options 45 | 46 | ## [4.0.3] - 2018-05-18 47 | 48 | ### Fixed 49 | 50 | - Multiple classes issue in className (#13) 51 | 52 | ## [4.0.2] - 2018-04-21 53 | 54 | ### Changed 55 | 56 | - Syntax of JS files 57 | - Placeholder images in demos 58 | 59 | ### Fixed 60 | 61 | - Videos and iframe content not clickable 62 | 63 | ## [4.0.1] - 2018-02-23 64 | 65 | ### Changed 66 | 67 | - Removed unnecessary `requestAnimationFrame` (#12) 68 | 69 | ## [4.0.0] - 2017-12-22 70 | 71 | ### Added 72 | 73 | - Support for `video` tag 74 | - Documentation of SASS variables 75 | 76 | ### Changed 77 | 78 | - Improved responsiveness when used with `iframe` tag 79 | - Syntax of JS and SCSS files 80 | 81 | ## [3.0.4] - 2017-08-08 82 | 83 | ### Changed 84 | 85 | - Ignore `yarn.lock` and `package-lock.json` files 86 | 87 | ## [3.0.3] - 2017-08-08 88 | 89 | ### Added 90 | 91 | - Added a changelog 92 | 93 | ### Fixed 94 | 95 | - `.99` opacity bug (#7) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Tobias Reich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # basicLightbox 2 | 3 | [![Donate via PayPal](https://img.shields.io/badge/paypal-donate-009cde.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CYKBESW577YWE) 4 | 5 | The lightest lightbox ever made. 6 | 7 | ## Contents 8 | 9 | - [Demos](#demos) 10 | - [Features](#features) 11 | - [Requirements](#requirements) 12 | - [Setup](#setup) 13 | - [API](#api) 14 | - [Instance API](#instance-api) 15 | - [Options](#options) 16 | 17 | ## Demos 18 | 19 | | Name | Description | Link | 20 | |:-----------|:------------|:------------| 21 | | Default | Includes most features. | [Try it on CodePen](https://codepen.io/electerious/pen/rLBvGz) | 22 | | DOM elements/nodes | Use DOM elements/nodes in basicLightbox. | [Try it on CodePen](https://codepen.io/electerious/pen/pOBLxQ) | 23 | | Create element | Use `.createElement()` with basicLightbox. | [Try it on CodePen](https://codepen.io/electerious/pen/wEZmQy) | 24 | | Events | Multiple ways to handle events. | [Try it on CodePen](https://codepen.io/electerious/pen/pOBLQQ) | 25 | 26 | ## Features 27 | 28 | - Works in all modern browsers and IE11 ([with polyfills](#requirements)) 29 | - Supports images, videos, iframes and any kind of HTML 30 | - Creates a lightbox from a string or from a DOM element/node 31 | - Zero dependencies 32 | - CommonJS and AMD support 33 | - Simple JS API 34 | 35 | ## Requirements 36 | 37 | basicLightbox depends on the following browser features and APIs: 38 | 39 | - [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) 40 | - [Node​List​.prototype​.for​Each](https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach) 41 | - [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 42 | - [window.requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) 43 | 44 | Some of these APIs are capable of being polyfilled in older browsers. Check the linked resources above to determine if you must polyfill to achieve your desired level of browser support. 45 | 46 | ## Setup 47 | 48 | We recommend installing basicLightbox using [npm](https://npmjs.com) or [yarn](https://yarnpkg.com). 49 | 50 | ```sh 51 | npm install basiclightbox 52 | ``` 53 | 54 | ```sh 55 | yarn add basiclightbox 56 | ``` 57 | 58 | Include the CSS file in the `head` tag and the JS file at the end of your `body` tag… 59 | 60 | ```html 61 | 62 | ``` 63 | 64 | ```html 65 | 66 | ``` 67 | 68 | …or skip the JS file and use basicLightbox as a module: 69 | 70 | ```js 71 | const basicLightbox = require('basiclightbox') 72 | ``` 73 | 74 | ```js 75 | import * as basicLightbox from 'basiclightbox' 76 | ``` 77 | 78 | ## API 79 | 80 | ### .create(content, opts) 81 | 82 | Creates a new basicLightbox instance. 83 | 84 | Be sure to assign your instance to a variable. Using your instance, you can… 85 | 86 | * …show and hide the lightbox. 87 | * …check if the the lightbox is visible. 88 | * …modify the content of the lightbox. 89 | 90 | Examples: 91 | 92 | ```js 93 | const instance = basicLightbox.create(` 94 |

Dynamic Content

95 |

You can set the content of the lightbox with JS.

96 | `) 97 | ``` 98 | 99 | ```js 100 | const instance = basicLightbox.create(` 101 |

Not closable

102 |

It's not possible to close this lightbox with a click.

103 | `, { 104 | closable: false 105 | }) 106 | ``` 107 | 108 | ```js 109 | const instance = basicLightbox.create( 110 | document.querySelector('#template') 111 | ) 112 | ``` 113 | 114 | Parameters: 115 | 116 | - `content` `{Node|String}` Content of the lightbox. 117 | - `opts` `{?Object}` An object of [options](#options). 118 | 119 | Returns: 120 | 121 | - `{Object}` The created instance. 122 | 123 | ### .visible() 124 | 125 | Returns `true` when a lightbox is visible. Also returns `true` when a lightbox is currently in the process of showing/hiding and not fully visible/hidden, yet. 126 | 127 | Example: 128 | 129 | ```js 130 | const visible = basicLightbox.visible() 131 | ``` 132 | 133 | Returns: 134 | 135 | - `{Boolean}` Visibility of any lightbox. 136 | 137 | ## Instance API 138 | 139 | Each basicLightbox instance has a handful of handy functions. Below are all of them along with a short description. 140 | 141 | ### .show(cb) 142 | 143 | Shows a lightbox instance. 144 | 145 | Examples: 146 | 147 | ```js 148 | instance.show() 149 | ``` 150 | 151 | ```js 152 | instance.show(() => console.log('lightbox now visible')) 153 | ``` 154 | 155 | Parameters: 156 | 157 | - `cb(instance)` `{?Function}` A function that gets executed as soon as the lightbox starts to fade in. 158 | 159 | ### .close(cb) 160 | 161 | Closes a lightbox instance. 162 | 163 | Examples: 164 | 165 | ```js 166 | instance.close() 167 | ``` 168 | 169 | ```js 170 | instance.close(() => console.log('lightbox not visible anymore')) 171 | ``` 172 | 173 | Parameters: 174 | 175 | - `cb(instance)` `{?Function}` A function that gets executed as soon as the lightbox has been faded out. 176 | 177 | ### .visible() 178 | 179 | Returns `true` when the lightbox instance is visible. Also returns `true` when the lightbox is currently in the process of showing/hiding and not fully visible/hidden, yet. 180 | 181 | Example: 182 | 183 | ```js 184 | const visible = instance.visible() 185 | ``` 186 | 187 | Returns: 188 | 189 | - `{Boolean}` Visibility of lightbox. 190 | 191 | ### .element() 192 | 193 | Returns the DOM element/node associated with the instance. 194 | 195 | Example: 196 | 197 | ```js 198 | const elem = instance.element() 199 | ``` 200 | 201 | Returns: 202 | 203 | - `{Node}` DOM element/node associated with the instance. 204 | 205 | ## Options 206 | 207 | The option object can include the following properties: 208 | 209 | ```js 210 | { 211 | /* 212 | * Prevents the lightbox from closing when clicking its background. 213 | */ 214 | closable: true, 215 | /* 216 | * One or more space separated classes to be added to the basicLightbox element. 217 | */ 218 | className: '', 219 | /* 220 | * Function that gets executed before the lightbox will be shown. 221 | * Returning false will prevent the lightbox from showing. 222 | */ 223 | onShow: (instance) => {}, 224 | /* 225 | * Function that gets executed before the lightbox closes. 226 | * Returning false will prevent the lightbox from closing. 227 | */ 228 | onClose: (instance) => {} 229 | } 230 | ``` 231 | 232 | Import `src/styles/main.scss` directly to customize the look of basicLightbox: 233 | 234 | ```scss 235 | $basicLightbox__background: rgba(0, 0, 0, .8); // Background color 236 | $basicLightbox__zIndex: 1000; // Stack order 237 | $basicLightbox__duration: .4s; // Transition duration 238 | $basicLightbox__timing: ease; // Transition timing 239 | 240 | @import 'src/styles/main'; 241 | ``` -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basicLightbox", 3 | "authors": [ 4 | "Tobias Reich " 5 | ], 6 | "description": "The lightest lightbox ever made", 7 | "main": [ 8 | "dist/basicLightbox.min.css", 9 | "dist/basicLightbox.min.js" 10 | ], 11 | "keywords": [ 12 | "lightbox", 13 | "popup", 14 | "modal", 15 | "window", 16 | "dialog", 17 | "gallery", 18 | "photo", 19 | "responsive", 20 | "mobile" 21 | ], 22 | "ignore": [ 23 | "**/.*", 24 | "demos", 25 | "build.js", 26 | "package.json" 27 | ], 28 | "license": "MIT", 29 | "homepage": "https://basiclightbox.electerious.com", 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/electerious/basicLightbox.git" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { writeFile } = require('fs').promises 4 | const js = require('rosid-handler-js') 5 | const sass = require('rosid-handler-sass') 6 | 7 | sass('src/styles/main.scss', { 8 | 9 | optimize: true 10 | 11 | }).then((data) => { 12 | 13 | return writeFile('dist/basicLightbox.min.css', data) 14 | 15 | }) 16 | 17 | js('src/scripts/main.js', { 18 | 19 | optimize: true, 20 | browserify: { 21 | standalone: 'basicLightbox' 22 | } 23 | 24 | }).then((data) => { 25 | 26 | return writeFile('dist/basicLightbox.min.js', data) 27 | 28 | }) -------------------------------------------------------------------------------- /demos/create.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | basicLightbox Demo 6 | 7 | 8 | 9 | 10 | 11 | 46 | 47 | 48 | 49 | 50 | 51 | 69 | 70 | -------------------------------------------------------------------------------- /demos/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | basicLightbox Demo 6 | 7 | 8 | 9 | 10 | 11 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 188 | 189 | -------------------------------------------------------------------------------- /demos/dom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | basicLightbox Demo 6 | 7 | 8 | 9 | 10 | 11 | 46 | 47 | 51 | 52 | 55 | 56 | 61 | 62 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 87 | 88 | -------------------------------------------------------------------------------- /demos/events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | basicLightbox Demo 6 | 7 | 8 | 9 | 10 | 11 | 46 | 47 | 52 | 53 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 108 | 109 | -------------------------------------------------------------------------------- /dist/basicLightbox.min.css: -------------------------------------------------------------------------------- 1 | .basicLightbox{position:fixed;display:flex;justify-content:center;align-items:center;top:0;left:0;width:100%;height:100vh;background:rgba(0,0,0,.8);opacity:.01;transition:opacity .4s ease;z-index:1000;will-change:opacity}.basicLightbox--visible{opacity:1}.basicLightbox__placeholder{max-width:100%;transform:scale(.9);transition:transform .4s ease;z-index:1;will-change:transform}.basicLightbox__placeholder>iframe:first-child:last-child,.basicLightbox__placeholder>img:first-child:last-child,.basicLightbox__placeholder>video:first-child:last-child{display:block;position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;max-width:95%;max-height:95%}.basicLightbox__placeholder>iframe:first-child:last-child,.basicLightbox__placeholder>video:first-child:last-child{pointer-events:auto}.basicLightbox__placeholder>img:first-child:last-child,.basicLightbox__placeholder>video:first-child:last-child{width:auto;height:auto}.basicLightbox--iframe .basicLightbox__placeholder,.basicLightbox--img .basicLightbox__placeholder,.basicLightbox--video .basicLightbox__placeholder{width:100%;height:100%;pointer-events:none}.basicLightbox--visible .basicLightbox__placeholder{transform:scale(1)} -------------------------------------------------------------------------------- /dist/basicLightbox.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).basicLightbox=e()}}((function(){return function e(n,t,o){function r(c,u){if(!t[c]){if(!n[c]){var s="function"==typeof require&&require;if(!u&&s)return s(c,!0);if(i)return i(c,!0);var a=new Error("Cannot find module '"+c+"'");throw a.code="MODULE_NOT_FOUND",a}var l=t[c]={exports:{}};n[c][0].call(l.exports,(function(e){return r(n[c][1][e]||e)}),l,l.exports,e,n,t,o)}return t[c].exports}for(var i="function"==typeof require&&require,c=0;c1&&void 0!==arguments[1]&&arguments[1],t=document.createElement("div");return t.innerHTML=e.trim(),!0===n?t.children:t.firstChild},r=function(e,n){var t=e.children;return 1===t.length&&t[0].tagName===n},i=function(e){return null!=(e=e||document.querySelector(".basicLightbox"))&&!0===e.ownerDocument.body.contains(e)};t.visible=i;t.create=function(e,n){var t=function(e,n){var t=o('\n\t\t
\n\t\t\t\n\t\t
\n\t')),i=t.querySelector(".basicLightbox__placeholder");e.forEach((function(e){return i.appendChild(e)}));var c=r(i,"IMG"),u=r(i,"VIDEO"),s=r(i,"IFRAME");return!0===c&&t.classList.add("basicLightbox--img"),!0===u&&t.classList.add("basicLightbox--video"),!0===s&&t.classList.add("basicLightbox--iframe"),t}(e=function(e){var n="string"==typeof e,t=e instanceof HTMLElement==1;if(!1===n&&!1===t)throw new Error("Content must be a DOM element/node or string");return!0===n?Array.from(o(e,!0)):"TEMPLATE"===e.tagName?[e.content.cloneNode(!0)]:Array.from(e.children)}(e),n=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(null==(e=Object.assign({},e)).closable&&(e.closable=!0),null==e.className&&(e.className=""),null==e.onShow&&(e.onShow=function(){}),null==e.onClose&&(e.onClose=function(){}),"boolean"!=typeof e.closable)throw new Error("Property `closable` must be a boolean");if("string"!=typeof e.className)throw new Error("Property `className` must be a string");if("function"!=typeof e.onShow)throw new Error("Property `onShow` must be a function");if("function"!=typeof e.onClose)throw new Error("Property `onClose` must be a function");return e}(n)),c=function(e){return!1!==n.onClose(u)&&function(e,n){return e.classList.remove("basicLightbox--visible"),setTimeout((function(){return!1===i(e)||e.parentElement.removeChild(e),n()}),410),!0}(t,(function(){if("function"==typeof e)return e(u)}))};!0===n.closable&&t.addEventListener("click",(function(e){e.target===t&&c()}));var u={element:function(){return t},visible:function(){return i(t)},show:function(e){return!1!==n.onShow(u)&&function(e,n){return document.body.appendChild(e),setTimeout((function(){requestAnimationFrame((function(){return e.classList.add("basicLightbox--visible"),n()}))}),10),!0}(t,(function(){if("function"==typeof e)return e(u)}))},close:c};return u}},{}]},{},[1])(1)})); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basiclightbox", 3 | "version": "5.0.4", 4 | "authors": [ 5 | "Tobias Reich " 6 | ], 7 | "description": "The lightest lightbox ever made", 8 | "main": "dist/basicLightbox.min.js", 9 | "keywords": [ 10 | "lightbox", 11 | "popup", 12 | "modal", 13 | "window", 14 | "dialog", 15 | "gallery", 16 | "photo", 17 | "responsive", 18 | "mobile" 19 | ], 20 | "scripts": { 21 | "build": "node build.js" 22 | }, 23 | "license": "MIT", 24 | "homepage": "https://basiclightbox.electerious.com", 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/electerious/basicLightbox.git" 28 | }, 29 | "files": [ 30 | "dist", 31 | "src" 32 | ], 33 | "devDependencies": { 34 | "rosid-handler-js": "^13.0.2", 35 | "rosid-handler-sass": "^8.0.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/scripts/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates an element from a HTML string. 3 | * @param {String} html 4 | * @param {?Boolean} children - Return all children instead of the first one. 5 | * @returns {Node} 6 | */ 7 | const toElement = function(html, children = false) { 8 | 9 | const elem = document.createElement('div') 10 | 11 | elem.innerHTML = html.trim() 12 | 13 | return children === true ? elem.children : elem.firstChild 14 | 15 | } 16 | 17 | /** 18 | * Validates and converts content. 19 | * @param {Node|String} content 20 | * @returns {Array} content - Validated content. 21 | */ 22 | const validateContent = function(content) { 23 | 24 | const isString = typeof content === 'string' 25 | const isHTMLElement = content instanceof HTMLElement === true 26 | 27 | if (isString === false && isHTMLElement === false) { 28 | 29 | throw new Error('Content must be a DOM element/node or string') 30 | 31 | } 32 | 33 | if (isString === true) { 34 | 35 | // String 36 | return Array.from(toElement(content, true)) 37 | 38 | } else if (content.tagName === 'TEMPLATE') { 39 | 40 | // Template 41 | return [ content.content.cloneNode(true) ] 42 | 43 | } else { 44 | 45 | // HTMLElement 46 | return Array.from(content.children) 47 | 48 | } 49 | 50 | } 51 | 52 | /** 53 | * Validates options and sets defaults for undefined properties. 54 | * @param {?Object} opts 55 | * @returns {Object} opts - Validated options. 56 | */ 57 | const validateOptions = function(opts = {}) { 58 | 59 | opts = Object.assign({}, opts) 60 | 61 | if (opts.closable == null) opts.closable = true 62 | if (opts.className == null) opts.className = '' 63 | if (opts.onShow == null) opts.onShow = () => {} 64 | if (opts.onClose == null) opts.onClose = () => {} 65 | 66 | if (typeof opts.closable !== 'boolean') throw new Error('Property `closable` must be a boolean') 67 | if (typeof opts.className !== 'string') throw new Error('Property `className` must be a string') 68 | if (typeof opts.onShow !== 'function') throw new Error('Property `onShow` must be a function') 69 | if (typeof opts.onClose !== 'function') throw new Error('Property `onClose` must be a function') 70 | 71 | return opts 72 | 73 | } 74 | 75 | /** 76 | * Checks if an element's first child has a specific tag. 77 | * @param {Node} elem 78 | * @param {String} tag 79 | * @returns {Boolean} containsTag 80 | */ 81 | const containsTag = function(elem, tag) { 82 | 83 | const children = elem.children 84 | 85 | return (children.length === 1 && children[0].tagName === tag) 86 | 87 | } 88 | 89 | /** 90 | * Checks if a given or any lightbox is visible. 91 | * @param {?Node} elem 92 | * @returns {Boolean} visible 93 | */ 94 | export const visible = function(elem) { 95 | 96 | elem = elem || document.querySelector('.basicLightbox') 97 | 98 | return (elem != null && elem.ownerDocument.body.contains(elem) === true) 99 | 100 | } 101 | 102 | /** 103 | * Creates a lightbox element. 104 | * @param {Array} content 105 | * @param {Object} opts 106 | * @returns {Node} elem 107 | */ 108 | const render = function(content, opts) { 109 | 110 | const elem = toElement(` 111 |
112 | 113 |
114 | `) 115 | 116 | const placeholder = elem.querySelector('.basicLightbox__placeholder') 117 | 118 | // Move content into lightbox placeholder 119 | content.forEach((child) => placeholder.appendChild(child)) 120 | 121 | // Check if placeholder contains a tag that requires a special treatment 122 | const img = containsTag(placeholder, 'IMG') 123 | const video = containsTag(placeholder, 'VIDEO') 124 | const iframe = containsTag(placeholder, 'IFRAME') 125 | 126 | // Add special treatment class when it only contains an image, a video or iframe. 127 | // This class is necessary to center the image, video or iframe. 128 | if (img === true) elem.classList.add('basicLightbox--img') 129 | if (video === true) elem.classList.add('basicLightbox--video') 130 | if (iframe === true) elem.classList.add('basicLightbox--iframe') 131 | 132 | return elem 133 | 134 | } 135 | 136 | /** 137 | * Shows a lightbox by appending an element to the DOM. 138 | * @param {Node} elem 139 | * @param {Function} next - The callback that gets executed when the lightbox starts to show up. 140 | * @returns {Boolean} success 141 | */ 142 | const show = function(elem, next) { 143 | 144 | document.body.appendChild(elem) 145 | 146 | // Wait a while to ensure that the class change triggers the animation 147 | setTimeout(() => { 148 | requestAnimationFrame(() => { 149 | 150 | elem.classList.add('basicLightbox--visible') 151 | 152 | return next() 153 | 154 | }) 155 | }, 10) 156 | 157 | return true 158 | 159 | } 160 | 161 | /** 162 | * Closes a lightbox by fading the element out and by removing the element from the DOM. 163 | * @param {Node} elem 164 | * @param {Function} next - The callback that gets executed when the lightbox is fully closed. 165 | * @returns {Boolean} success 166 | */ 167 | const close = function(elem, next) { 168 | 169 | elem.classList.remove('basicLightbox--visible') 170 | 171 | setTimeout(() => { 172 | 173 | // Don't continue to remove lightbox when element missing 174 | if (visible(elem) === false) return next() 175 | 176 | elem.parentElement.removeChild(elem) 177 | 178 | return next() 179 | 180 | }, 410) 181 | 182 | return true 183 | 184 | } 185 | 186 | /** 187 | * Creats a new instance. 188 | * @param {Node|String} content 189 | * @param {?Object} opts 190 | * @returns {Object} instance 191 | */ 192 | export const create = function(content, opts) { 193 | 194 | content = validateContent(content) 195 | opts = validateOptions(opts) 196 | 197 | // Render the lightbox element 198 | const elem = render(content, opts) 199 | 200 | // Returns the lightbox element 201 | const _element = () => { 202 | 203 | return elem 204 | 205 | } 206 | 207 | // Check if the lightbox is attached to the DOM 208 | const _visible = () => { 209 | 210 | return visible(elem) 211 | 212 | } 213 | 214 | // Show the lightbox 215 | const _show = (next) => { 216 | 217 | // Run onShow callback and stop execution when function returns false 218 | if (opts.onShow(instance) === false) return false 219 | 220 | // Show the lightbox 221 | return show(elem, () => { 222 | 223 | // Continue with the callback when available 224 | if (typeof next === 'function') return next(instance) 225 | 226 | }) 227 | 228 | } 229 | 230 | // Hide the lightbox 231 | const _close = (next) => { 232 | 233 | // Run onClose callback and stop execution when function returns false 234 | if (opts.onClose(instance) === false) return false 235 | 236 | return close(elem, () => { 237 | 238 | // Continue with the callback when available 239 | if (typeof next === 'function') return next(instance) 240 | 241 | }) 242 | 243 | } 244 | 245 | // Close lightbox when clicking the background 246 | if (opts.closable === true) elem.addEventListener('click', (e) => { 247 | 248 | // If e.target is not the same element as elem, 249 | // then the user clicked a descendant of the element. 250 | if (e.target !== elem) return 251 | 252 | // Close lightbox with the instance function 253 | _close() 254 | 255 | }) 256 | 257 | // Assign instance to a variable so the instance can be used 258 | // elsewhere in the current function. 259 | const instance = { 260 | element: _element, 261 | visible: _visible, 262 | show: _show, 263 | close: _close 264 | } 265 | 266 | return instance 267 | 268 | } -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | // Vars ---------------------------------------------------------------- // 2 | $basicLightbox__background: rgba(0, 0, 0, .8) !default; 3 | $basicLightbox__zIndex: 1000 !default; 4 | $basicLightbox__duration: .4s !default; 5 | $basicLightbox__timing: ease !default; 6 | 7 | // basicLightbox ------------------------------------------------------- // 8 | .basicLightbox { 9 | 10 | position: fixed; 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | top: 0; 15 | left: 0; 16 | width: 100%; 17 | height: 100vh; 18 | background: $basicLightbox__background; 19 | opacity: .01; // Start with .01 to avoid the repaint that happens from 0 to .01 20 | transition: opacity $basicLightbox__duration $basicLightbox__timing; 21 | z-index: $basicLightbox__zIndex; 22 | will-change: opacity; 23 | 24 | &--visible { 25 | opacity: 1; 26 | } 27 | 28 | &__placeholder { 29 | max-width: 100%; 30 | transform: scale(.9); 31 | transition: transform $basicLightbox__duration $basicLightbox__timing; 32 | z-index: 1; 33 | will-change: transform; 34 | 35 | > img:first-child:last-child, 36 | > video:first-child:last-child, 37 | > iframe:first-child:last-child { 38 | display: block; 39 | position: absolute; 40 | top: 0; 41 | right: 0; 42 | bottom: 0; 43 | left: 0; 44 | margin: auto; 45 | max-width: 95%; 46 | max-height: 95%; 47 | } 48 | 49 | > video:first-child:last-child, 50 | > iframe:first-child:last-child { 51 | pointer-events: auto; 52 | } 53 | 54 | > img:first-child:last-child, 55 | > video:first-child:last-child { 56 | width: auto; 57 | height: auto; 58 | } 59 | } 60 | 61 | &--img &__placeholder, 62 | &--video &__placeholder, 63 | &--iframe &__placeholder { 64 | width: 100%; 65 | height: 100%; 66 | pointer-events: none; 67 | } 68 | 69 | &--visible &__placeholder { 70 | transform: scale(1); 71 | } 72 | 73 | } --------------------------------------------------------------------------------