Bootstrap(v4)目前没有输入标记功能。虽然存在提供标记功能的库(例如TagsInput和Select2 - 特别是对于选择元素),但它们都不提供我想要的完全功能;即,textarea
中typeahead支持的标签垂直堆叠。虽然可以修改Bloodhound
并且这些库可以这样做,但鉴于我想亲自添加的其他一些功能,我认为从头开始会更容易。另外,作为一个新的[Bootstrap]用户,我非常喜欢他们轻巧但功能性的javascript,并想花点时间尝试从中学习。另外,虽然我写了很多javascript,但我不是jQuery用户,所以这也为jQuery提供了一个跳跃点。
虽然制作功能标记库不在任何单个S.O.的范围内。问题是,制作一个新的引导类(在现有类中占据重要地位)应该在范围内。
如果我们考虑“标签”是什么 - 在输入的上下文中 - 它只是用户提供的输入包含在某种形式的块/泡沫中,通常附近有一个可忽略的按钮。 Bootstrap(v4)有一个类已经实现了大部分风格特征 - alerts。提供的链接是源代码。本质上,为了构建Alert类,我可以继承或只是复制粘贴并用“tag”替换所有“alert”实例。为了便于阅读,可以在帖子的底部找到这样做的结果。
作为jQuery的初学者,有很多我不了解他们的代码。即使编写了大量的javascript,我也从未使用过导入和导入语句 - 这在尝试使用代码时会导致错误。
所以我继续使用Util源代码,从util.js
复制粘贴了所需的三个功能:
Util.TRANSITION_END,
Util.getTransitionDurationFromElement(element),
Util.getSelectorFromElement(element)
我认为我现在很高兴,假设已经加载了jQuery。 事实证明事实并非如此。我有错误:
TypeError: element.getAttribute is not a function
tag.js:67:24
其中第67行是粘贴的Util.getSelectorFromElement(element)
函数的第一行:
let selector = element.getAttribute('data-target')
因此,简而言之,我的问题是,如何将我的“新”类(将警告类复制到新文件并将其重命名为标记类)合并到我的本地版Bootstrap中?
对Bootstrap使用的类结构的任何其他见解也将受到赞赏。
const Tag = (($) => {
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'tag'
const VERSION = '0'
const DATA_KEY = `bs.tag`
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Selector = {
DISMISS : '[data-dismiss="tag"]'
}
const Event = {
CLOSE : `close${EVENT_KEY}`,
CLOSED : `closed${EVENT_KEY}`,
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
TAG: 'tag',
FADE: 'fade',
SHOW: 'show'
}
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Tag {
constructor(element) {
this._element = element
}
// Getters
static get VERSION() {
return VERSION
}
// Public
close(element) {
element = element || this._element
const rootElement = this._getRootElement(element)
const customEvent = this._triggerCloseEvent(rootElement)
if (customEvent.isDefaultPrevented()) {
return
}
this._removeElement(rootElement)
}
dispose() {
$.removeData(this.element, DATA_KEY)
this._element = null
}
// Private
_getRootElement(element) {
const selector = function(element) {
let selector = element.getAttribute('data-target')
if (!selector || selector === '#') {
selector = element.getAttribute('href') || ''
}
try {
const $selector = $(document).find(selector)
return $selector.length > 0 ? selector : null
} catch (err) {
return null
}
}
// const selector = Util.getSelectorFromElement(element)
let parent = false
if (selector) {
parent = $(selector)[0]
}
if (!parent) {
parent = $(element).closest(`.${ClassName.TAG}`)[0]
}
return parent
}
_triggerCloseEvent(element) {
const closeEvent = $.Event(Event.CLOSE)
$(element).trigger(closeEvent)
return closeEvent
}
_removeElement(element) {
$(element).removeClass(ClassName.SHOW)
if (!$(element).hasClass(ClassName.FADE)) {
this._destroyElement(element)
return
}
const transitionDuration = function(element) {
if (!element) {
return 0
}
// Get transition-duration of the element
let transitionDuration = $(element).css('transition-duration')
const floatTransitionDuration = parseFloat(transitionDuration)
// Return 0 if element or transition duration is not found
if (!floatTransitionDuration) {
return 0
}
// If multiple durations are defined, take the first
transitionDuration = transitionDuration.split(',')[0]
return parseFloat(transitionDuration) * MILLISECONDS_MULTIPLIER
}
// const transitionDuration = Util.getTransitionDurationFromElement(element)
$(element)
.one('transitionend', (event) => this._destroyElement(element, event))
.emulateTransitionEnd(transitionDuration)
// .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))
}
_destroyElement(element) {
$(element)
.detach()
.trigger(Event.CLOSED)
.remove()
}
// Static
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
if (!data) {
data = new Alert(this)
$element.data(DATA_KEY, data)
}
if (config === 'close') {
data[config](this)
}
})
}
static _handleDismiss(tagInstance) {
return function (event) {
if (event) {
event.preventDefault()
}
tagInstance.close(this)
}
}
}
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(
Event.CLICK_DATA_API,
Selector.DISMISS,
Tag._handleDismiss(new Tag())
)
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
$.fn[NAME] = Tag._jQueryInterface
$.fn[NAME].Constructor = Tag
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Tag._jQueryInterface
}
return Tag
})($)
// export default Tag