Skip to content

Instantly share code, notes, and snippets.

@vralle
Last active July 30, 2022 10:39
Show Gist options
  • Save vralle/0d34689f7e683198eaea to your computer and use it in GitHub Desktop.
Save vralle/0d34689f7e683198eaea to your computer and use it in GitHub Desktop.
jQuery offcanvas.js (based on Bootstrap Modal.js)

#offcanvas.js

##Usage

The offcanvas plugin toggles your hidden content on demand, via data attributes or JavaScript. It also adds .offcanvas-open to the <body> to override default scrolling behavior and generates a .offcanvas-backdrop to provide a click area for dismissing shown offcanvas when clicking outside the offcanvas.

<!-- Button trigger offcanvas -->
<button class="btn btn-primary" data-toggle="offcanvas" data-target="#myOffcanvas" data-fade="false">
  Launch offcanvas
</button>
<!-- Offcanvas -->
<div class="offcanvas" id="myOffcanvas" role="navigation" aria-labelledby="myOffcanvasLabel" aria-hidden="true">
  <button class="btn btn-primary" data-dismiss="offcanvas">Close</button>
  Offcanvas
</div

###Via data attributes

Activate a offcanvas without writing JavaScript. Set data-toggle="offcanvas" on a controller element, like a button, along with a data-target="#foo" or href="#foo" to target a specific offcanvas to toggle.

###Via JavaScript

Call a offcanvas with id myOffcanvas with a single line of JavaScript:

$('#myOffcanvas').offcanvas(options)

###Options

Options can be passed via data attributes or JavaScript. For data attributes, append the option name to data-, as in data-fade="false".

Name type default description
fade boolean true Includes a offcanvas-backdrop element. Alternatively, specify false for a disable backdrop overlay.
keyboard boolean true Closes the offcanvas when escape key is pressed
show boolean true Shows the offcanvas when initialized.

###Methods

####.offcanvas(options) Activates your content as a offcanvas. Accepts an optional options object.

$('#myOffcanvas').offcanvas({keyboard : false})

####.offcanvas('toggle')

Manually toggles a offcanvas. Returns to the caller before the offcanvas has actually been shown or hidden (i.e. before the shown.vr.offcanvas or hidden.vr.offcanvas event occurs).

$('#myOffcanvas').offcanvas('toggle')

####.offcanvas('show') Manually opens a offcanvas. Returns to the caller before the offcanvas has actually been shown (i.e. before the shown.vr.offcanvas event occurs).

$('#myOffcanvas').offcanvas('show')

####.offcanvas('hide') Manually hides a offcanvas. Returns to the caller before the offcanvas has actually been hidden (i.e. before the hidden.vr.offcanvas event occurs).

$('#myOffcanvas').offcanvas('hide')

###Events Offcanvas class exposes a few events for hooking into offcanvas functionality.

Event Type Description
show.vr.offcanvas This event fires immediately when the show instance method is called.
shown.vr.offcanvas This event is fired when the offcanvas has been made visible to the user (will wait for CSS transitions to complete).
hide.vr.offcanvas This event is fired immediately when the hide instance method has been called.
hidden.vr.offcanvas This event is fired when the offcanvas has finished being hidden from the user (will wait for CSS transitions to complete).

##Copyright and license Code and documentation copyright 2014 Vitaliy Ralle. Code released under the MIT license. Docs released under Creative Commons.

.offcanvas-open {
overflow: hidden;
}
.offcanvas {
position: fixed;
top: 0;
bottom: 0;
z-index: 1050;
max-width: 70%;
overflow: hidden;
-webkit-overflow-scrolling: touch;
outline: 0;
background-color: #f6f6f6;
visibility: hidden;
opacity: 0;
filter: alpha(opacity=0);
}
.offcanvas.in {
visibility: visible;
opacity: 1;
filter: alpha(opacity=100);
}
.use-csstransforms3d .offcanvas {
-webkit-transition: all .3s ease-out;
-o-transition: all .3s ease-out;
transition: all .3s ease-out;
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
}
.use-csstransforms3d .offcanvas.in {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.use-no-csstransforms3d .offcanvas {
left: -170px;
-webkit-transition: left .3s ease-out;
-o-transition: left .3s ease-out;
transition: left .3s ease-out;
}
.use-no-csstransforms3d .offcanvas.in {
left: 0;
}
.offcanvas-open .offcanvas {
overflow-x: hidden;
overflow-y: auto;
}
.offcanvas-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 900;
background-color: black;
opacity: 0;
filter: alpha(opacity=0);
}
.offcanvas-backdrop.fade.in {
opacity: 0.5;
filter: alpha(opacity=50);
}
.offcanvas-scrollbar-measure {
position: absolute;
top: -9999px;
width: 50px;
height: 50px;
overflow: scroll;
}
/* Offcanvas.js | Inspired by the original Bootstrap: Modal.js by Twitter, Inc. | Copyright 2014 Vitaliy Ralle <[email protected]> (https://twitter.com/_vralle) | Licensed under MIT */
+function ($) {
'use strict';
// OFFCANVAS CLASS DEFINITION
// ======================
var Offcanvas = function (element, options) {
this.options = options
this.$body = $(document.body)
this.$element = $(element)
this.$backdrop =
this.isShown = null
this.scrollbarWidth = 0
}
Offcanvas.VERSION = '0.4.1'
Offcanvas.TRANSITION_DURATION = 300
Offcanvas.BACKDROP_TRANSITION_DURATION = 150
Offcanvas.DEFAULTS = {
backdrop: true,
keyboard: true,
show: true,
fade: true
}
Offcanvas.prototype.toggle = function () {
return this.isShown ? this.hide() : this.show()
}
Offcanvas.prototype.show = function () {
var that = this
var e = $.Event('show.vr.offcanvas')
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
this.isShown = true
this.checkScrollbar()
this.$body.addClass('offcanvas-open')
this.setScrollbar()
this.escape()
this.$element.on('click.dismiss.vr.offcanvas', '[data-dismiss="offcanvas"]', $.proxy(this.hide, this))
this.backdrop(function () {
var transition = $.support.transition
that.$element
.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element
.addClass('in')
.attr('aria-hidden', false)
var e = $.Event('shown.vr.offcanvas')
transition
?
that.$element
.one('bsTransitionEnd', function () {
that.$element.trigger(e)
})
.emulateTransitionEnd(Offcanvas.TRANSITION_DURATION)
:
that.$element.trigger(e)
})
}
Offcanvas.prototype.hide = function (e) {
if (e) e.preventDefault()
e = $.Event('hide.vr.offcanvas')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
this.$body.removeClass('offcanvas-open')
this.resetScrollbar()
this.escape()
this.$element
.removeClass('in')
.attr('aria-hidden', true)
.off('click.dismiss.bs.offcanvas')
$.support.transition
?
this.$element
.one('bsTransitionEnd', $.proxy(this.hideOffcanvas, this))
.emulateTransitionEnd(Offcanvas.TRANSITION_DURATION)
:
this.hideOffcanvas()
}
Offcanvas.prototype.escape = function () {
if (this.isShown && this.options.keyboard) {
this.$body.on('keydown.dismiss.vr.offcanvas', $.proxy(function (e) {
e.which == 27 && this.hide()
}, this))
} else if (!this.isShown) {
this.$body.off('keydown.dismiss.vr.offcanvas')
}
}
Offcanvas.prototype.hideOffcanvas = function () {
var that = this
this.$element.hide()
this.backdrop(function () {
that.$element.trigger('hidden.vr.offcanvas')
})
}
Offcanvas.prototype.removeBackdrop = function () {
this.$backdrop && this.$backdrop.remove()
this.$backdrop = null
}
Offcanvas.prototype.backdrop = function (callback) {
var that = this
var fade = this.options.fade ? ' fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && fade
this.$backdrop = $('<div class="offcanvas-backdrop' + fade + '" />')
.appendTo(this.$body)
// Close Off-canvas by click on $backdrop
this.$backdrop.on('mousedown.dismiss.vr.offcanvas', $.proxy(function (e) {
if (e.target !== e.currentTarget) return
this.hide.call(this)
}, this))
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
if (!callback) return
doAnimate
?
this.$backdrop
.one('bsTransitionEnd', callback)
.emulateTransitionEnd(Offcanvas.BACKDROP_TRANSITION_DURATION)
:
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
var callbackRemove = function () {
that.removeBackdrop()
callback && callback()
}
$.support.transition
?
this.$backdrop
.one('bsTransitionEnd', callbackRemove)
.emulateTransitionEnd(Offcanvas.BACKDROP_TRANSITION_DURATION)
:
callbackRemove()
} else if (callback) {
callback()
}
}
Offcanvas.prototype.checkScrollbar = function () {
if (document.body.clientWidth >= window.innerWidth) return
this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar()
}
Offcanvas.prototype.setScrollbar = function () {
var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
}
Offcanvas.prototype.resetScrollbar = function () {
this.$body.css('padding-right', '')
}
Offcanvas.prototype.measureScrollbar = function () { // thx walsh
var scrollDiv = document.createElement('div')
scrollDiv.className = 'offcanvas-scrollbar-measure'
this.$body.append(scrollDiv)
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
this.$body[0].removeChild(scrollDiv)
return scrollbarWidth
}
// OFFCANVAS PLUGIN DEFINITION
// =======================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('vr.offcanvas')
var options = $.extend({}, Offcanvas.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('vr.offcanvas', (data = new Offcanvas(this, options)))
if (typeof option == 'string') data[option]()
else if (options.show) data.show()
})
}
var old = $.fn.offcanvas
$.fn.offcanvas = Plugin
$.fn.offcanvas.Constructor = Offcanvas
// OFFCANVAS NO CONFLICT
// =================
$.fn.offcanvas.noConflict = function () {
$.fn.offcanvas = old
return this
}
// OFFCANVAS DATA-API
// ==============
$(document).on('click.vr.offcanvas.data-api', '[data-offcanvas="toggle"]', function (e) {
var $this = $(this)
var href = $this.attr('href')
var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
var option = $target.data('vr.offcanvas') ? 'toggle' : $.extend($target.data(), $this.data())
if ($this.is('a')) e.preventDefault()
Plugin.call($target, option)
})
}(jQuery);
@mabnawp
Copy link

mabnawp commented Feb 1, 2022

Thank you very much, it was really practical and great

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment