iuap-design
Version:
UI Framework Used For Enterprise.
223 lines (198 loc) • 9.62 kB
JavaScript
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;
}
};