Bootstrap v4:理解和添加自定义类

时间:2018-03-24 08:16:16

标签: javascript jquery html twitter-bootstrap bootstrap-4

动机

Bootstrap(v4)目前没有输入标记功能。虽然存在提供标记功能的库(例如TagsInputSelect2 - 特别是对于选择元素),但它们都不提供我想要的完全功能;即,textareatypeahead支持的标签垂直堆叠。虽然可以修改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')

问题(S)

因此,简而言之,我的问题是,如何将我的“新”类(将警告类复制到新文件并将其重命名为标记类)合并到我的本地版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

0 个答案:

没有答案