Tooltips component
I. Introduction
-
This component uses the Tether plug-in, which is generally used to determine the location relationship between two elements, as follows:
new Tether({ // This is the active attachment element element: blurEle, // This is the dependent element. target: greenEle, // Both active and passive attachment can choose their locations. Both of them determine a point through x and y. // Finally, the two points coincide. // Active attachment attachment: 'bottom center', // Passive attachment targetAttachment: 'top center' })
So what appears is that the lower left corner of the blue element coincides with the top middle of the green element.
- In the case of delay, i.e. delayed pop-up (recovery), and assuming that the trigger event is click, the tooltip does not come out after one click, and the next click is made immediately. At this time, instead of resetting the counter, the pop-up of the tooltip is cancelled.
-
The initial template for this component is
<div class="tooltip" role="tooltip"> <div class="tooltip-inner"> </div> </div>
The simplest use is to specify the title attribute on the element tag that needs to trigger tooltip, and then initialize it with $(). tooltip().
- When the tooltip element is hidden, it is removed from the document, and each time it appears, it is inserted into the document again. After the element is inserted into the document, the INSERTED event is triggered.
Two, style
The main style is two classes: tooltip + tooltip-inner
About the inner. tooltip-inner
.tooltip-inner {
// The maximum width is specified. If you want to add elements internally, you need to pay attention to this limitation.
max-width: $tooltip-max-width;
... ...
}
Three, script
Scripts are mainly used to control the hiding and display of tooltip. Users can change the way elements trigger tooltip by passing in objects in initialization functions. They can be click, hover and focus.
Default attribute
// Here are the default attributes that users can selectively override
const Default = {
// Is there animation?
animation : true,
// Toolltip templates, which typically insert content into'.tooltip-inner'
template : '<div class="tooltip" role="tooltip">'
+ '<div class="tooltip-inner"></div></div>',
// How to trigger tooltip
trigger : 'hover focus',
// It can be a dom element, a jquery object, or a function (this points to the dependent element)
// Note that here is the data-title attribute
title : '',
// The incoming object {show: 500, hide: 300} is in milliseconds or an integer
delay : 0,
// Is pop-up tooltip allowed to be rendered as html nodes (content)
// Because the user can assign a value to a node or function for the data-title attribute, if html===false, it will eventually be displayed.
// The content of template is $(node). text(), otherwise it is the whole $(node)
html : false,
// Event proxy, namely $(' a a a'). tooltip ({selector: $('. B B b')}), requires B to be a child of a, and ultimately triggers tooltip.
// That's the. bbb element. The effect is consistent with the event proxy, which can dynamically add elements containing bbb classes with tooltip effect.
selector : false,
// Location of tooltip display
placement : 'top',
// (belongs to the tether plug-in) The offset tether, by default, binds the two elements tightly together. If you want to display a small triangle like tooltip, you must use it
offset : '0 0',
// (belongs to tether plug-in) unknown
constraints : [],
// The tooltip element is generated from nothing and does not exist in the document when hidden.
// Which parent element will tooltip be placed under when this element is displayed? The default is body
container : false
}
It is worth noting that if you want to add html content to tooltip by making html == true, you may need to modify the styles of. tooltip and. tooltip-inner classes manually.
Code outline
class Tooltip {
constructor(element, config) {
// Is it activated?
this._isEnabled = true
// The return value of settimeout of deal for clear
this._timeout = 0
// Used to control when tooltip is in the process of opening animation, trigger the _leave function, although because _isTransitioning will not execute hide function, but
// Change the value of this._hoverState to HoverState.OUT
// If this state is HoverState.OUT after tooltip really opens, then the _leave function will still be executed.
this._hoverState = ''
// Determine what the current activation state is activated by
this._activeTrigger = {}
this._isTransitioning = false
this._tether = null
// protected
// Point to the element that triggers tooltip
this.element = element
this.config = this._getConfig(config)
// Point to the tooltip element
this.tip = null
// Event-bound callback functions for triggering tooltip
this._setListeners()
}
// public common function
// Make this component available or unavailable
toggleEnabled() {}
// Hide or reveal tooltip
toggle(event) {}
// auxiliary function
// Determine if there is content in tooltip
isWithContent() {}
// If it's the first time, take the template; otherwise, take the previously hidden tooltip element
getTipElement() {}
// Place title content or data-title content under the. tooltip-inner element
setContent() {}
// Get the value of the title attribute or data-title attribute
getTitle() {}
// Binding trigger events to trigger hooks for tooltip
_setListeners() {}
// The click event calls this function: _enter(null, context)
// Hover, focus event call: _enter(event)
_enter(event, context) {}
_leave(event, context) {}
// static
// Initialization entry
static _jQueryInterface(config) {
return this.each(function () {})
}
}
From an overview of the code of the entire component (not posted in part), it can be seen that there are three kinds of events that trigger tooltip: click, hover, focusin; different events call different callbacks (only discussing making tooltip appear):
- click: toggle(event) ==> _enter(null, context) ==> show()
- hover or focusin: _enter(event) ==> show()
Another place is the _setListeners function, through which you can discover the role of selector attributes
_setListeners() {
// The way to trigger tooltip can be separated by multiple spaces
const triggers = this.config.trigger.split(' ')
// Register different event callbacks according to different trigger modes
triggers.forEach((trigger) => {
// click event registration toggle function
// jQuery.on function, if the delegate element this.config.selector is null, the event is bound to $(this.element)
if (trigger === 'click') {
$(this.element).on(..., (event) => this.toggle(event))
} else if (trigger !== Trigger.MANUAL) {
...
// hover and focus event registration_enter,_leave function
$(this.element)
.on(..., (event) => this._enter(event)
)
.on(..., (event) => this._leave(event)
)
}
// If placed in Modal, Modal will also turn off these tools after hide
$(this.element).closest('.modal').on(
'hide.bs.modal',
() => this.hide()
)
})
// If you find the element that actually triggers tooltip when you initialize this instance
// It should be the child element this.config.selector
if (this.config.selector) {
this.config = $.extend({}, this.config, {
trigger : 'manual',
selector : ''
})
} else {
// It's this element that triggers tooltip, so it handles its title attribute (not data-title)
this._fixTitle()
}
}
The properties of selector can be seen as the second parameter for event delegation binding $(parent). on ('click',?, func), which explains why selector must be a child of the $(parent) element. You can also find that there are no instances created for the elements that actually trigger tooltip, but later by
We still need to discuss the _fixTitle function, which allows users to put content in title attributes into tooltip. If the element that triggers tooltip has a title attribute, the function clears the attribute and ports the attribute value to the attribute data-original-title (because the title attribute itself prompts when the mouse hover, which removes the negative effect while preserving the title value)
CLICK event callback
Since the callback of the click event covers all the main functions, we will focus on the callback process of the click open tool event.
-
Call the toggle(event) function.
- If it is triggered by click event (click event passes event parameter), the tag causes the tooltip to pop up (if hover trigger is supported at the same time, hover out will not remove the tooltip if there is a click trigger tag at this time), as long as there is any tag of click trgger or hover trigger or focus trigger, the _enter function is triggered.
- If the user js triggers manually, the trigger_enter is selected based on whether the tooltip element has a. show class or not.
-
Call the _enter(event, context) function
- If triggered by a hover or focus event (click events do not pass event parameters), mark the hover trigger or focus trigger.
Make another tag, HoverState, where the tag needs to open the tooltip; then determine whether there is a delay attribute value, or call setTimeout to delay the opening of the tooltip.
-
Call show() function
- Trigger SHOW hook
- Get the tooltip element, change its id value to a random value (every pop-up changes), and insert the content of the title or data-title attribute into the. tooltip-inner element in the tooltip element.
- Bind Tooltip instances for tooltip elements (shared with trigger elements)
- Insert the tooltip element into the document to trigger the INSERTED hook
- Use Tether to place tooltip in the specified location
- Add a show class for tooltip (if animated) to trigger the SHOWN hook after the animation is finished
- If the mouse leaves this element during the animation and executes the _leave function, then _hoverState becomes out, although this time hide function will not be executed because of the existence of _isTransitioning. In the callback after the animation is finished, the _hoverStat state is judged, and if out, the _leave function is executed.
Details review
-
Get the filling content of tooltip
- Get it according to the title attribute. The _fixTitle attribute introduced at the beginning of the article has introduced the processing of title attribute and the reasons for processing it.
- Get it according to the data-title attribute.
// Get content getTitle() { // Get title, if not data-title let title = this.element.getAttribute('data-original-title') if (!title) { title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title } return title } // Insert content setElementContent($element, content) { const html = this.config.html // You can specify a node for the data-title attribute to insert template if (typeof content === 'object' && (content.nodeType || content.jquery)) { // content is a DOM node or a jQuery // If you don't have to worry about XSS attacks, you can insert a node if (html) { if (!$(content).parent().is($element)) { $element.empty().append(content) } } else { // Don't allow HTML content to be added directly if you're worried. $element.text($(content).text()) } } else { // You can also specify html content on the title attribute (similar to this'<div></div>') $element[html ? 'html' : 'text'](content) } }
Supplementary knowledge
-
Array.from Converts Class Arrays into Array Rules
- array-like objects (objects with a length property and indexed elements)
- iterable objects (objects where you can get its elements, such as Map and Set).
- The following generates an array from 0 to 100:
// Grammar: Array.from(arrayLike[, mapFn[, thisArg]) Array.from({length:100}).map((item, index) => index) // Or Array.from(obj, mapFn, thisArg) has the same result as Array.from(obj).map(mapFn, thisArg) Array.from({length:100}, (item, index)=>index) // The third parameter thisArg is used to change this pointing in map functions Array.from([1, 2, 3], function(x){return x + this.a},{a:3}) // Don't use the arrow function of es6 above. The arrow function this inherits from the context.
The jQuery selector mostly acquires the Array-Like type, which can be converted to an array type (that is, Array.from ($('xxx')) instance of Array === true) by Array. After conversion, it is no longer a jQuery type, but a DOM type.
However, jQuery itself has this function, namely $('xxx').get(), whose parameter can be an array subscript ($(this).get(0) and $(this)[0]), which can be converted to a DOM array without filling in the subscript. - bootstrap provides a class of. disabled, indicating that it is not available, but it still triggers events, requiring control in the function through $(this).hasClass('disabled').