mirror of
https://github.com/twbs/bootstrap.git
synced 2025-09-26 05:19:15 +02:00
tooltip without jquery
This commit is contained in:
committed by
XhmikosR
parent
7c1d0a1097
commit
cc6e130fc1
@@ -9,8 +9,10 @@ import {
|
||||
DefaultWhitelist,
|
||||
sanitizeHtml
|
||||
} from './tools/sanitizer'
|
||||
import $ from 'jquery'
|
||||
import Data from './dom/data'
|
||||
import EventHandler from './dom/eventHandler'
|
||||
import Popper from 'popper.js'
|
||||
import SelectorEngine from './dom/selectorEngine'
|
||||
import Util from './util'
|
||||
|
||||
/**
|
||||
@@ -23,11 +25,11 @@ const NAME = 'tooltip'
|
||||
const VERSION = '4.3.1'
|
||||
const DATA_KEY = 'bs.tooltip'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
const CLASS_PREFIX = 'bs-tooltip'
|
||||
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
|
||||
const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
|
||||
|
||||
|
||||
const DefaultType = {
|
||||
animation : 'boolean',
|
||||
template : 'string',
|
||||
@@ -193,14 +195,14 @@ class Tooltip {
|
||||
|
||||
if (event) {
|
||||
const dataKey = this.constructor.DATA_KEY
|
||||
let context = $(event.currentTarget).data(dataKey)
|
||||
let context = Data.getData(event.delegateTarget, dataKey)
|
||||
|
||||
if (!context) {
|
||||
context = new this.constructor(
|
||||
event.currentTarget,
|
||||
this._getDelegateConfig()
|
||||
)
|
||||
$(event.currentTarget).data(dataKey, context)
|
||||
Data.setData(event.delegateTarget, dataKey, context)
|
||||
}
|
||||
|
||||
context._activeTrigger.click = !context._activeTrigger.click
|
||||
@@ -211,7 +213,7 @@ class Tooltip {
|
||||
context._leave(null, context)
|
||||
}
|
||||
} else {
|
||||
if ($(this.getTipElement()).hasClass(ClassName.SHOW)) {
|
||||
if (this.getTipElement().classList.contains(ClassName.SHOW)) {
|
||||
this._leave(null, this)
|
||||
return
|
||||
}
|
||||
@@ -223,13 +225,13 @@ class Tooltip {
|
||||
dispose() {
|
||||
clearTimeout(this._timeout)
|
||||
|
||||
$.removeData(this.element, this.constructor.DATA_KEY)
|
||||
Data.removeData(this.element, this.constructor.DATA_KEY)
|
||||
|
||||
$(this.element).off(this.constructor.EVENT_KEY)
|
||||
$(this.element).closest('.modal').off('hide.bs.modal')
|
||||
EventHandler.off(this.element, this.constructor.EVENT_KEY)
|
||||
EventHandler.off(SelectorEngine.closest(this.element, '.modal'), 'hide.bs.modal')
|
||||
|
||||
if (this.tip) {
|
||||
$(this.tip).remove()
|
||||
this.tip.parentNode.removeChild(this.tip)
|
||||
}
|
||||
|
||||
this._isEnabled = null
|
||||
@@ -247,21 +249,18 @@ class Tooltip {
|
||||
}
|
||||
|
||||
show() {
|
||||
if ($(this.element).css('display') === 'none') {
|
||||
if (this.element.style.display === 'none') {
|
||||
throw new Error('Please use show on visible elements')
|
||||
}
|
||||
|
||||
const showEvent = $.Event(this.constructor.Event.SHOW)
|
||||
if (this.isWithContent() && this._isEnabled) {
|
||||
$(this.element).trigger(showEvent)
|
||||
|
||||
const showEvent = EventHandler.trigger(this.element, this.constructor.Event.SHOW)
|
||||
const shadowRoot = Util.findShadowRoot(this.element)
|
||||
const isInTheDom = $.contains(
|
||||
shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement,
|
||||
this.element
|
||||
)
|
||||
const isInTheDom = shadowRoot !== null
|
||||
? shadowRoot.contains(this.element)
|
||||
: this.element.ownerDocument.documentElement.contains(this.element)
|
||||
|
||||
if (showEvent.isDefaultPrevented() || !isInTheDom) {
|
||||
if (showEvent.defaultPrevented || !isInTheDom) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -274,7 +273,7 @@ class Tooltip {
|
||||
this.setContent()
|
||||
|
||||
if (this.config.animation) {
|
||||
$(tip).addClass(ClassName.FADE)
|
||||
tip.classList.add(ClassName.FADE)
|
||||
}
|
||||
|
||||
const placement = typeof this.config.placement === 'function'
|
||||
@@ -285,13 +284,13 @@ class Tooltip {
|
||||
this.addAttachmentClass(attachment)
|
||||
|
||||
const container = this._getContainer()
|
||||
$(tip).data(this.constructor.DATA_KEY, this)
|
||||
Data.setData(tip, this.constructor.DATA_KEY, this)
|
||||
|
||||
if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
|
||||
$(tip).appendTo(container)
|
||||
if (!this.element.ownerDocument.documentElement.contains(this.tip)) {
|
||||
container.appendChild(tip)
|
||||
}
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.INSERTED)
|
||||
EventHandler.trigger(this.element, this.constructor.Event.INSERTED)
|
||||
|
||||
this._popper = new Popper(this.element, tip, {
|
||||
placement: attachment,
|
||||
@@ -315,14 +314,16 @@ class Tooltip {
|
||||
onUpdate: (data) => this._handlePopperPlacementChange(data)
|
||||
})
|
||||
|
||||
$(tip).addClass(ClassName.SHOW)
|
||||
tip.classList.add(ClassName.SHOW)
|
||||
|
||||
// If this is a touch-enabled device we add extra
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
// only needed because of broken event delegation on iOS
|
||||
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
$(document.body).children().on('mouseover', null, $.noop)
|
||||
Util.makeArray(document.body.children).forEach((element) => {
|
||||
EventHandler.on(element, 'mouseover', Util.noop)
|
||||
})
|
||||
}
|
||||
|
||||
const complete = () => {
|
||||
@@ -332,20 +333,18 @@ class Tooltip {
|
||||
const prevHoverState = this._hoverState
|
||||
this._hoverState = null
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.SHOWN)
|
||||
EventHandler.trigger(this.element, this.constructor.Event.SHOWN)
|
||||
|
||||
if (prevHoverState === HoverState.OUT) {
|
||||
this._leave(null, this)
|
||||
}
|
||||
}
|
||||
|
||||
if ($(this.tip).hasClass(ClassName.FADE)) {
|
||||
if (this.tip.classList.contains(ClassName.FADE)) {
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(this.tip)
|
||||
|
||||
$(this.tip)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
|
||||
Util.emulateTransitionEnd(tip, transitionDuration)
|
||||
EventHandler.one(this.tip, Util.TRANSITION_END, complete)
|
||||
Util.emulateTransitionEnd(this.tip, transitionDuration)
|
||||
} else {
|
||||
complete()
|
||||
}
|
||||
@@ -354,15 +353,14 @@ class Tooltip {
|
||||
|
||||
hide(callback) {
|
||||
const tip = this.getTipElement()
|
||||
const hideEvent = $.Event(this.constructor.Event.HIDE)
|
||||
const complete = () => {
|
||||
const complete = () => {
|
||||
if (this._hoverState !== HoverState.SHOW && tip.parentNode) {
|
||||
tip.parentNode.removeChild(tip)
|
||||
}
|
||||
|
||||
this._cleanTipClass()
|
||||
this.element.removeAttribute('aria-describedby')
|
||||
$(this.element).trigger(this.constructor.Event.HIDDEN)
|
||||
EventHandler.trigger(this.element, this.constructor.Event.HIDDEN)
|
||||
if (this._popper !== null) {
|
||||
this._popper.destroy()
|
||||
}
|
||||
@@ -372,30 +370,29 @@ class Tooltip {
|
||||
}
|
||||
}
|
||||
|
||||
$(this.element).trigger(hideEvent)
|
||||
|
||||
if (hideEvent.isDefaultPrevented()) {
|
||||
const hideEvent = EventHandler.trigger(this.element, this.constructor.Event.HIDE)
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return
|
||||
}
|
||||
|
||||
$(tip).removeClass(ClassName.SHOW)
|
||||
tip.classList.remove(ClassName.SHOW)
|
||||
|
||||
// If this is a touch-enabled device we remove the extra
|
||||
// empty mouseover listeners we added for iOS support
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
$(document.body).children().off('mouseover', null, $.noop)
|
||||
Util.makeArray(document.body.children)
|
||||
.forEach((element) => EventHandler.off(element, 'mouseover', Util.noop))
|
||||
}
|
||||
|
||||
this._activeTrigger[Trigger.CLICK] = false
|
||||
this._activeTrigger[Trigger.FOCUS] = false
|
||||
this._activeTrigger[Trigger.HOVER] = false
|
||||
|
||||
if ($(this.tip).hasClass(ClassName.FADE)) {
|
||||
if (this.tip.classList.contains(ClassName.FADE)) {
|
||||
const transitionDuration = Util.getTransitionDurationFromElement(tip)
|
||||
|
||||
$(tip)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(transitionDuration)
|
||||
EventHandler.one(tip, Util.TRANSITION_END, complete)
|
||||
Util.emulateTransitionEnd(tip, transitionDuration)
|
||||
} else {
|
||||
complete()
|
||||
}
|
||||
@@ -416,29 +413,46 @@ class Tooltip {
|
||||
}
|
||||
|
||||
addAttachmentClass(attachment) {
|
||||
$(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`)
|
||||
this.getTipElement().classList.add(`${CLASS_PREFIX}-${attachment}`)
|
||||
}
|
||||
|
||||
getTipElement() {
|
||||
this.tip = this.tip || $(this.config.template)[0]
|
||||
if (this.tip) {
|
||||
return this.tip
|
||||
}
|
||||
|
||||
const element = document.createElement('div')
|
||||
element.innerHTML = this.config.template
|
||||
|
||||
this.tip = element.children[0]
|
||||
return this.tip
|
||||
}
|
||||
|
||||
setContent() {
|
||||
const tip = this.getTipElement()
|
||||
this.setElementContent($(tip.querySelectorAll(Selector.TOOLTIP_INNER)), this.getTitle())
|
||||
$(tip).removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)
|
||||
this.setElementContent(SelectorEngine.findOne(Selector.TOOLTIP_INNER, tip), this.getTitle())
|
||||
tip.classList.remove(ClassName.FADE)
|
||||
tip.classList.remove(ClassName.SHOW)
|
||||
}
|
||||
|
||||
setElementContent($element, content) {
|
||||
setElementContent(element, content) {
|
||||
if (element === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof content === 'object' && (content.nodeType || content.jquery)) {
|
||||
// Content is a DOM node or a jQuery
|
||||
if (content.jquery) {
|
||||
content = content[0]
|
||||
}
|
||||
|
||||
// content is a DOM node or a jQuery
|
||||
if (this.config.html) {
|
||||
if (!$(content).parent().is($element)) {
|
||||
$element.empty().append(content)
|
||||
if (content.parentNode !== element) {
|
||||
element.innerHTML = ''
|
||||
element.appendChild(content)
|
||||
}
|
||||
} else {
|
||||
$element.text($(content).text())
|
||||
element.innerText = content.textContent
|
||||
}
|
||||
|
||||
return
|
||||
@@ -449,9 +463,9 @@ class Tooltip {
|
||||
content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)
|
||||
}
|
||||
|
||||
$element.html(content)
|
||||
element.innerHTML = content
|
||||
} else {
|
||||
$element.text(content)
|
||||
element.innerText = content
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,10 +508,10 @@ class Tooltip {
|
||||
}
|
||||
|
||||
if (Util.isElement(this.config.container)) {
|
||||
return $(this.config.container)
|
||||
return this.config.container
|
||||
}
|
||||
|
||||
return $(document).find(this.config.container)
|
||||
return SelectorEngine.findOne(this.config.container)
|
||||
}
|
||||
|
||||
_getAttachment(placement) {
|
||||
@@ -509,7 +523,7 @@ class Tooltip {
|
||||
|
||||
triggers.forEach((trigger) => {
|
||||
if (trigger === 'click') {
|
||||
$(this.element).on(
|
||||
EventHandler.on(this.element,
|
||||
this.constructor.Event.CLICK,
|
||||
this.config.selector,
|
||||
(event) => this.toggle(event)
|
||||
@@ -522,21 +536,20 @@ class Tooltip {
|
||||
? this.constructor.Event.MOUSELEAVE
|
||||
: this.constructor.Event.FOCUSOUT
|
||||
|
||||
$(this.element)
|
||||
.on(
|
||||
eventIn,
|
||||
this.config.selector,
|
||||
(event) => this._enter(event)
|
||||
)
|
||||
.on(
|
||||
eventOut,
|
||||
this.config.selector,
|
||||
(event) => this._leave(event)
|
||||
)
|
||||
EventHandler.on(this.element,
|
||||
eventIn,
|
||||
this.config.selector,
|
||||
(event) => this._enter(event)
|
||||
)
|
||||
EventHandler.on(this.element,
|
||||
eventOut,
|
||||
this.config.selector,
|
||||
(event) => this._leave(event)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
$(this.element).closest('.modal').on(
|
||||
EventHandler.on(SelectorEngine.closest(this.element, '.modal'),
|
||||
'hide.bs.modal',
|
||||
() => {
|
||||
if (this.element) {
|
||||
@@ -571,14 +584,14 @@ class Tooltip {
|
||||
|
||||
_enter(event, context) {
|
||||
const dataKey = this.constructor.DATA_KEY
|
||||
context = context || $(event.currentTarget).data(dataKey)
|
||||
context = context || Data.getData(event.delegateTarget, dataKey)
|
||||
|
||||
if (!context) {
|
||||
context = new this.constructor(
|
||||
event.currentTarget,
|
||||
event.delegateTarget,
|
||||
this._getDelegateConfig()
|
||||
)
|
||||
$(event.currentTarget).data(dataKey, context)
|
||||
Data.setData(event.delegateTarget, dataKey, context)
|
||||
}
|
||||
|
||||
if (event) {
|
||||
@@ -587,7 +600,8 @@ class Tooltip {
|
||||
] = true
|
||||
}
|
||||
|
||||
if ($(context.getTipElement()).hasClass(ClassName.SHOW) || context._hoverState === HoverState.SHOW) {
|
||||
if (context.getTipElement().classList.contains(ClassName.SHOW) ||
|
||||
context._hoverState === HoverState.SHOW) {
|
||||
context._hoverState = HoverState.SHOW
|
||||
return
|
||||
}
|
||||
@@ -610,14 +624,14 @@ class Tooltip {
|
||||
|
||||
_leave(event, context) {
|
||||
const dataKey = this.constructor.DATA_KEY
|
||||
context = context || $(event.currentTarget).data(dataKey)
|
||||
context = context || Data.getData(event.delegateTarget, dataKey)
|
||||
|
||||
if (!context) {
|
||||
context = new this.constructor(
|
||||
event.currentTarget,
|
||||
event.delegateTarget,
|
||||
this._getDelegateConfig()
|
||||
)
|
||||
$(event.currentTarget).data(dataKey, context)
|
||||
Data.setData(event.delegateTarget, dataKey, context)
|
||||
}
|
||||
|
||||
if (event) {
|
||||
@@ -657,7 +671,7 @@ class Tooltip {
|
||||
}
|
||||
|
||||
_getConfig(config) {
|
||||
const dataAttributes = $(this.element).data()
|
||||
const dataAttributes = Util.getDataAttributes(this.element)
|
||||
|
||||
Object.keys(dataAttributes)
|
||||
.forEach((dataAttr) => {
|
||||
@@ -666,6 +680,11 @@ class Tooltip {
|
||||
}
|
||||
})
|
||||
|
||||
if (typeof config !== 'undefined' &&
|
||||
typeof config.container === 'object' && config.container.jquery) {
|
||||
config.container = config.container[0]
|
||||
}
|
||||
|
||||
config = {
|
||||
...this.constructor.Default,
|
||||
...dataAttributes,
|
||||
@@ -715,10 +734,12 @@ class Tooltip {
|
||||
}
|
||||
|
||||
_cleanTipClass() {
|
||||
const $tip = $(this.getTipElement())
|
||||
const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX)
|
||||
const tip = this.getTipElement()
|
||||
const tabClass = tip.getAttribute('class').match(BSCLS_PREFIX_REGEX)
|
||||
if (tabClass !== null && tabClass.length) {
|
||||
$tip.removeClass(tabClass.join(''))
|
||||
tabClass
|
||||
.map((token) => token.trim())
|
||||
.forEach((tClass) => tip.classList.remove(tClass))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,7 +758,7 @@ class Tooltip {
|
||||
return
|
||||
}
|
||||
|
||||
$(tip).removeClass(ClassName.FADE)
|
||||
tip.classList.remove(ClassName.FADE)
|
||||
this.config.animation = false
|
||||
this.hide()
|
||||
this.show()
|
||||
@@ -748,7 +769,7 @@ class Tooltip {
|
||||
|
||||
static _jQueryInterface(config) {
|
||||
return this.each(function () {
|
||||
let data = $(this).data(DATA_KEY)
|
||||
let data = Data.getData(this, DATA_KEY)
|
||||
const _config = typeof config === 'object' && config
|
||||
|
||||
if (!data && /dispose|hide/.test(config)) {
|
||||
@@ -757,7 +778,7 @@ class Tooltip {
|
||||
|
||||
if (!data) {
|
||||
data = new Tooltip(this, _config)
|
||||
$(this).data(DATA_KEY, data)
|
||||
Data.setData(this, DATA_KEY, data)
|
||||
}
|
||||
|
||||
if (typeof config === 'string') {
|
||||
@@ -775,12 +796,15 @@ class Tooltip {
|
||||
* jQuery
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
$.fn[NAME] = Tooltip._jQueryInterface
|
||||
$.fn[NAME].Constructor = Tooltip
|
||||
$.fn[NAME].noConflict = () => {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Tooltip._jQueryInterface
|
||||
const $ = Util.jQuery
|
||||
if (typeof $ !== 'undefined') {
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
$.fn[NAME] = Tooltip._jQueryInterface
|
||||
$.fn[NAME].Constructor = Tooltip
|
||||
$.fn[NAME].noConflict = () => {
|
||||
$.fn[NAME] = JQUERY_NO_CONFLICT
|
||||
return Tooltip._jQueryInterface
|
||||
}
|
||||
}
|
||||
|
||||
export default Tooltip
|
||||
|
Reference in New Issue
Block a user