UNPKG

iuap-design

Version:
223 lines (198 loc) 9.62 kB
u.Tooltip = function(element,options){ this.init(element,options) //this.show() } u.Tooltip.prototype = { defaults:{ animation: true, placement: 'top', //selector: false, template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow" ></div><div class="tooltip-inner"></div></div>', trigger: 'hover focus', title: '', delay: 0, html: false, container: false, viewport: { selector: 'body', padding: 0 } }, init: function (element,options) { this.element = element this.options = u.extend({}, this.defaults, options); this._viewport = this.options.viewport && document.querySelector(this.options.viewport.selector || this.options.viewport); var triggers = this.options.trigger.split(' ') for (var i = triggers.length; i--;) { var trigger = triggers[i] if (trigger == 'click') { u.on(this.element, 'click', this.toggle.bind(this)); } else if (trigger != 'manual') { var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' u.on(this.element, eventIn, this.enter.bind(this)); u.on(this.element, eventOut, this.leave.bind(this)); } } this.options.title = this.options.title || this.element.getAttribute('title'); this.element.removeAttribute('title'); if (this.options.delay && typeof this.options.delay == 'number') { this.options.delay = { show: this.options.delay, hide: this.options.delay } }; //tip模板对应的dom this.tipDom = u.makeDOM(this.options.template); u.addClass(this.tipDom,this.options.placement); if(this.options.colorLevel){ u.addClass(this.tipDom,this.options.colorLevel); } this.arrrow = this.tipDom.querySelector('.tooltip-arrow'); // tip容器,默认为当前元素的parent this.container = this.options.container ? document.querySelector(this.options.container) : this.element.parentNode; }, enter: function(){ var self = this; clearTimeout(this.timeout); this.hoverState = 'in'; if (!this.options.delay || !this.options.delay.show) return this.show(); this.timeout = setTimeout(function () { if (self.hoverState == 'in') self.show() }, this.options.delay.show) }, leave: function(){ var self = this; clearTimeout(this.timeout); self.hoverState = 'out' if (!self.options.delay || !self.options.delay.hide) return self.hide() self.timeout = setTimeout(function () { if (self.hoverState == 'out') self.hide() }, self.options.delay.hide) }, show: function(){ var self = this; this.tipDom.querySelector('.tooltip-inner').innerHTML = this.options.title; this.tipDom.style.zIndex = u.getZIndex(); this.container.appendChild(this.tipDom); /*var placement = this.options.placement; var pos = this.getPosition() var actualWidth = this.tipDom.offsetWidth var actualHeight = this.tipDom.offsetHeight var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) this.applyPlacement(calculatedOffset, placement)*/ u.addClass(this.tipDom,'active'); u.showPanelByEle({ ele:this.element, panel:this.tipDom, position:this.options.placement }); document.body.onscroll = function(){ u.showPanelByEle({ ele:self.element, panel:self.tipDom, position:self.options.placement }); } }, hide: function(){ if (this.container.contains(this.tipDom)){ u.removeClass(this.tipDom, 'active'); this.container.removeChild(this.tipDom); } }, applyPlacement: function(offset, placement){ var width = this.tipDom.offsetWidth var height = this.tipDom.offsetHeight // manually read margins because getBoundingClientRect includes difference var marginTop = parseInt(this.tipDom.style.marginTop, 10) var marginLeft = parseInt(this.tipDom.style.marginTop, 10) // we must check for NaN for ie 8/9 if (isNaN(marginTop)) marginTop = 0 if (isNaN(marginLeft)) marginLeft = 0 offset.top = offset.top + marginTop offset.left = offset.left + marginLeft // $.fn.offset doesn't round pixel values // so we use setOffset directly with our own function B-0 this.tipDom.style.left = offset.left + 'px'; this.tipDom.style.top = offset.top + 'px'; u.addClass(this.tipDom,'active'); // check to see if placing tip in new offset caused the tip to resize itself var actualWidth = this.tipDom.offsetWidth var actualHeight =this.tipDom.offsetHeight if (placement == 'top' && actualHeight != height) { offset.top = offset.top + height - actualHeight } var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) if (delta.left) offset.left += delta.left else offset.top += delta.top var isVertical = /top|bottom/.test(placement) var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' //$tip.offset(offset) this.tipDom.style.left = offset.left + 'px'; this.tipDom.style.top = offset.top - 4 + 'px'; // this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) }, getCalculatedOffset: function(placement, pos, actualWidth, actualHeight){ return placement == 'bottom' ? {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} : placement == 'top' ? {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} : placement == 'left' ? {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth} : /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } }, getPosition: function(el){ el = el || this.element; var isBody = el.tagName == 'BODY'; var elRect = el.getBoundingClientRect() if (elRect.width == null) { // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 elRect = u.extend({}, elRect, {width: elRect.right - elRect.left, height: elRect.bottom - elRect.top}) } var elOffset = isBody ? {top: 0, left: 0} : {top:el.offsetTop, left: el.offsetLeft}; var scroll = {scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : el.scrollTop} var outerDims = isBody ? {width: window.innerWidth || document.body.clientWidth, height: window.innerHeight || document.body.clientHeight} : null //return u.extend({}, elRect, scroll, outerDims, elOffset) return u.extend({}, elRect, scroll, outerDims) }, getViewportAdjustedDelta: function(placement, pos, actualWidth, actualHeight){ var delta = {top: 0, left: 0} if (!this._viewport) return delta var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 var viewportDimensions = this.getPosition(this._viewport) if (/right|left/.test(placement)) { var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight if (topEdgeOffset < viewportDimensions.top) { // top overflow delta.top = viewportDimensions.top - topEdgeOffset } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset } } else { var leftEdgeOffset = pos.left - viewportPadding var rightEdgeOffset = pos.left + viewportPadding + actualWidth if (leftEdgeOffset < viewportDimensions.left) { // left overflow delta.left = viewportDimensions.left - leftEdgeOffset } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset } } return delta }, replaceArrow: function(delta, dimension, isHorizontal){ if (isHorizontal){ this.arrow.style.left = 50 * (1 - delta / dimension) + '%'; this.arrow.style.top = ''; }else{ this.arrow.style.top = 50 * (1 - delta / dimension) + '%'; this.arrow.style.left = ''; } }, destory: function(){ }, setTitle :function(title){ this.options.title = title; } };