Last active
December 16, 2015 02:49
-
-
Save brianfeister/5365416 to your computer and use it in GitHub Desktop.
Constrained popover to extend Bootstrap popover and make it intelligently switch orientation based on relative position of popup to container
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if ( ! $.fn.constrained_popover ) { | |
// Constrained popover allows you to pass any DOM element in jQuery syntax and get a | |
// Bootstrap popover that changes orientation based on it's proximity to the container bounds | |
/* CONSTRAINED_POPOVER PUBLIC CLASS DEFINITION | |
* =========================================== */ | |
var ConstrainedPopover = function ( element, options ) { | |
this.init('constrained_popover', element, options ) | |
}; | |
/* NOTE: CONSTRAINED_POPOVER EXTENDS BOOTSTRAP-POPOVER.js | |
========================================== */ | |
ConstrainedPopover.prototype = $.extend( {}, $.fn.popover.Constructor.prototype, { | |
constructor: ConstrainedPopover | |
, show: function () { | |
var $tip | |
, inside | |
, pos | |
, newPos | |
, actualWidth | |
, actualHeight | |
, placement | |
, tp | |
, finalPos = {} | |
if (this.hasContent() && this.enabled) { | |
$tip = this.tip() | |
this.setContent() | |
if (this.options.animation) { | |
$tip.addClass('fade') | |
} | |
placement = typeof this.options.placement == 'function' ? | |
this.options.placement.call(this, $tip[0], this.$element[0]) : | |
this.options.placement | |
inside = /in/.test(placement) | |
$tip | |
.remove() | |
.css({ top: 0, left: 0, display: 'block' }) | |
.appendTo(inside ? this.$element : document.body) | |
pos = this.getPosition(inside) | |
actualWidth = $tip[0].offsetWidth | |
actualHeight = $tip[0].offsetHeight | |
switch (inside ? placement.split(' ')[1] : placement) { | |
case 'left': | |
newPos = this.defineBounds( pos ) | |
if ( typeof newPos.top === "undefined" ) { | |
finalPos["top"] = pos.top + pos.height / 2 - actualHeight / 2 | |
} else { | |
finalPos["top"] = newPos.top - actualHeight / 2 | |
} | |
if ( typeof newPos.left === "undefined" ) { | |
finalPos["left"] = pos.left - actualWidth | |
} else { | |
finalPos["left"] = newPos.left - actualWidth | |
} | |
tp = { top: finalPos.top , left: finalPos.left } | |
break | |
case 'right': | |
newPos = this.defineBounds( pos ) | |
if ( typeof newPos.top === "undefined" ) { | |
finalPos["top"] = pos.top + pos.height / 2 - actualHeight / 2 | |
} else { | |
finalPos["top"] = newPos.top - actualHeight / 2 | |
} | |
if ( typeof newPos.left === "undefined" ) { | |
finalPos["left"] = pos.left + pos.width | |
} else { | |
finalPos["left"] = newPos.left + pos.width | |
} | |
tp = { top: finalPos.top , left: finalPos.left } | |
break | |
} | |
$tip | |
.css(tp) | |
.addClass(placement) | |
.addClass('in') | |
} | |
} | |
, defineBounds: function ( pos ) { | |
var container | |
, containerOffset | |
, boundTop | |
, boundLeft | |
, boundBottom | |
, boundRight | |
, newPos = {} | |
if ( $(this.options.container).length !== 0 ) { | |
// verify there is no special "inner-container" with checkMultiContainer() | |
container = this.checkMultiContainer() | |
containerOffset = container.offset() | |
boundTop = containerOffset.top | |
boundLeft = containerOffset.left | |
boundBottom = boundTop + container.height() | |
boundRight = boundLeft + container.width() | |
// Constrain y-axis overflow | |
if ( pos.top + ( pos.height / 2 ) < boundTop ) { | |
newPos["top"] = boundTop | |
} | |
if ( pos.top + ( pos.height / 2 ) > boundBottom ) { | |
newPos["top"] = boundBottom | |
} | |
// Constrain x-axis overflow | |
if ( pos.left - ( pos.width / 2 ) < boundLeft ) { | |
newPos["left"] = boundLeft | |
} | |
if ( pos.left - ( pos.width / 2 ) > boundRight ) { | |
newPos["left"] = boundRight | |
} | |
return newPos | |
} | |
else { | |
return false | |
} | |
} | |
, checkMultiContainer: function () { | |
var container | |
, containerNum | |
container = $( this.options.container ) | |
if ( container.length ) { | |
if ( container.length > 1 ) { | |
container = this.$element.closest( container ) | |
} | |
return container | |
} else { | |
return | |
} | |
} | |
}) | |
/* CONSTRAINED_POPOVER PLUGIN DEFINITION | |
* ===================================== */ | |
$.fn.constrained_popover = function ( option ) { | |
return this.each( function () { | |
var $this = $(this) | |
, data = $this.data('constrained_popover') | |
, options = typeof option == 'object' && option | |
if (!data) $this.data('constrained_popover', (data = new ConstrainedPopover(this, options))) | |
if (typeof option == 'string') data[option]() | |
}) | |
} | |
$.fn.constrained_popover.Constructor = ConstrainedPopover | |
$.fn.constrained_popover.defaults = $.extend({} , $.fn.popover.defaults, { | |
container: '' | |
, content: this.options | |
}) | |
} // END: CONSTRAINED_POPOVER | |
} ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment