From e68f4d0d170f8ffcfcfc5ede342a41953de6291c Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Thu, 3 Nov 2011 20:05:01 +0100 Subject: [PATCH] Fixed balloon tooltip Tooltip now aligns centered, left or right depending on free space to viewport bounds. @Luzifr: The arrow looks pretty ugly if it is aligned left or right caused by border-radius (shared corner) set for the tooltip itself. --- wcfsetup/install/files/js/WCF.js | 187 +++++++++++++++++++++++-------- 1 file changed, 143 insertions(+), 44 deletions(-) diff --git a/wcfsetup/install/files/js/WCF.js b/wcfsetup/install/files/js/WCF.js index 725003d684..da85bfea86 100644 --- a/wcfsetup/install/files/js/WCF.js +++ b/wcfsetup/install/files/js/WCF.js @@ -2664,66 +2664,165 @@ WCF.Effect.SmoothScroll.prototype = { */ WCF.Effect.BalloonTooltip = function() { this.init(); }; WCF.Effect.BalloonTooltip.prototype = { + /** + * initialization state + * @var boolean + */ + _didInit: false, + + /** + * tooltip element + * @var jQuery + */ + _tooltip: null, + + /** + * cache viewport dimensions + * @var object + */ + _viewportDimensions: { }, + + /** + * Initializes tooltips. + */ init: function() { - // create empty div - this.tooltip = $('
').appendTo(document.body).hide(); - + if (!this._didInit) { + // create empty div + this._tooltip = $('
').appendTo($('body')).hide(); + + // get viewport dimensions + this._updateViewportDimensions(); + + // update viewport dimensions on resize + $(window).resize($.proxy(this._updateViewportDimensions, this)); + + // observe DOM changes + WCF.DOMNodeInsertedHandler.addCallback('WCF.Effect.BallonTooltip', $.proxy(this.init, this)); + + this._didInit = true; + } + // init elements - $('.balloonTooltip').live('mouseenter', $.proxy(this._initTooltip, this)); + $('.balloonTooltip').each($.proxy(this._initTooltip, this)); + }, + + /** + * Updates cached viewport dimensions. + */ + _updateViewportDimensions: function() { + this._viewportDimensions = $(document).getDimensions(); }, - _initTooltip: function(event) { - $(event.currentTarget).die('mouseenter'); - $(event.currentTarget).removeClass('balloonTooltip'); - - $(event.currentTarget).hover( - $.proxy(this._mouseEnterHandler, this), - $.proxy(this._mouseLeaveHandler, this) - ); + /** + * Initializes a tooltip element. + * + * @param integer index + * @param object element + */ + _initTooltip: function(index, element) { + var $element = $(element); - this._mouseEnterHandler(event); + if ($element.hasClass('balloonTooltip')) { + $element.removeClass('balloonTooltip'); + var $title = $element.attr('title'); + + // ignore empty elements + if ($title !== '') { + $element.data('tooltip', $title); + $element.removeAttr('title') + + $element.hover( + $.proxy(this._mouseEnterHandler, this), + $.proxy(this._mouseLeaveHandler, this) + ); + } + } }, + /** + * Shows tooltip on hover. + * + * @param object event + */ _mouseEnterHandler: function(event) { var $element = $(event.currentTarget); - if ($element.attr('title')) { - $element.data('tooltip', $element.attr('title')); - $element.attr('title', ''); - // update text - this.tooltip.find('#balloonTooltipText').text($element.data('tooltip')); - - // calculate position - var $elementOffsets = $element.getOffsets('offset'); - var $elementDimensions = $element.getDimensions('outer'); - var $tooltipDimensions = this.tooltip.getDimensions('outer'); - - var $top = $elementOffsets.top + $elementDimensions.height; - var $left = $elementOffsets.left - ($tooltipDimensions.width / 2) + ($elementDimensions.width / 2); + // update text + this._tooltip.children('span:eq(0)').text($element.data('tooltip')); + + // get arrow + var $arrow = this._tooltip.find('.arrowOuter'); + + // get arrow width + this._tooltip.show(); + var $arrowWidth = $arrow.outerWidth(); + this._tooltip.hide(); - this.tooltip.css({ - top: ($top + 7) + "px", - left: ($left) + "px" - }); - - // arrow positioning - this.tooltip.find('.arrowOuter').css({ - left: (this.tooltip.getDimensions().width / 2 + 3) + "px" - }); + // calculate position + var $elementOffsets = $element.getOffsets('offset'); + var $elementDimensions = $element.getDimensions('outer'); + var $tooltipDimensions = this._tooltip.getDimensions('outer'); + var $tooltipDimensionsInner = this._tooltip.getDimensions('inner'); + + var $elementCenter = $elementOffsets.left + Math.ceil($elementDimensions.width / 2); + var $tooltipHalfWidth = Math.ceil($tooltipDimensions.width / 2); - // show tooltip - this.tooltip.fadeIn('fast'); + // determine alignment + $alignment = 'center'; + if (($elementCenter - $tooltipHalfWidth) < 5) { + $alignment = 'left'; + } + else if ((this._viewportDimensions.width - 5) < ($elementCenter + $tooltipHalfWidth)) { + $alignment = 'right'; } + + // calculate top offset + var $top = $elementOffsets.top + $elementDimensions.height + 7; + + // calculate left offset + switch ($alignment) { + case 'center': + var $left = Math.round($elementOffsets.left - $tooltipHalfWidth + ($elementDimensions.width / 2)); + + $arrow.css({ + left: ($tooltipDimensionsInner.width / 2 - $arrowWidth / 2) + "px" + }); + break; + + case 'left': + var $left = $elementOffsets.left; + + $arrow.css({ + left: "0px" + }); + break; + + case 'right': + var $left = $elementOffsets.left + $elementDimensions.width - $tooltipDimensions.width; + + $arrow.css({ + left: ($tooltipDimensionsInner.width - $arrowWidth) + "px" + }); + break; + } + + // move tooltip + this._tooltip.css({ + top: $top + "px", + left: $left + "px" + }); + + // show tooltip + this._tooltip.fadeIn('fast'); }, + /** + * Hides tooltip once cursor left the element. + * + * @param object event + */ _mouseLeaveHandler: function(event) { - var $element = $(event.currentTarget); - if ($element.data('tooltip')) { - $element.attr('title', $element.data('tooltip')); - $element.data('tooltip', ''); - - this.tooltip.hide(); - } + this._tooltip.hide(); } }; -- 2.20.1