> For the complete documentation index, see [llms.txt](https://learncss.gitbook.io/cssadvand/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://learncss.gitbook.io/cssadvand/1-giao-dien-su-dung-thuoc-tinh-data-coreui-toggle-dropdown-ok.md).

# 1 Giao diện sử dụng thuộc tính data-coreui-toggle="dropdown" (ok)

Source code

{% file src="/files/p2TIdOyfBDQi9brEIRQX" %}

<figure><img src="/files/fBPi8G1tmhdbJnMnw52L" alt=""><figcaption></figcaption></figure>

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" />
  <link rel="stylesheet" href="//code.jquery.com/jquery-3.7.1.min.js">
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.2/css/bootstrap.min.css"/>
  <script src="dist/js/coreui.bundle.js"></script>
</head>
<body>
  <div class="dropdown btn-group">
    <button class="btn btn-secondary dropdown-toggle" type="button" data-coreui-toggle="dropdown" aria-expanded="false">
      <i class="fas fa-cog"></i>
    </button>
    <ul class="dropdown-menu">
      <li>
        <a class="dropdown-item" href='@'>
          <i class="fas fa-eye-slash"></i> View trash
        </a>
      </li>
    </ul>
  </div>
</body>
</html>
```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\index.umd.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): index.esm.js
 * Licensed under MIT (https://coreui.io/license)
 * --------------------------------------------------------------------------
 */
import Dropdown from './src/dropdown'
export default {
  Dropdown
}
```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dropdown.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dropdown.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's dropdown.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */
import * as Popper from '@popperjs/core'
import {
  defineJQueryPlugin,
  getElement,
  getElementFromSelector,
  getNextActiveElement,
  isDisabled,
  isElement,
  isRTL,
  isVisible,
  noop,
  typeCheckConfig
} from './util/index'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
const NAME = 'dropdown'
const DATA_KEY = 'coreui.dropdown'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const ESCAPE_KEY = 'Escape'
const SPACE_KEY = 'Space'
const TAB_KEY = 'Tab'
const ARROW_UP_KEY = 'ArrowUp'
const ARROW_DOWN_KEY = 'ArrowDown'
const RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`)
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_DROPUP = 'dropup'
const CLASS_NAME_DROPEND = 'dropend'
const CLASS_NAME_DROPSTART = 'dropstart'
const CLASS_NAME_NAVBAR = 'navbar'
const SELECTOR_DATA_TOGGLE = '[data-coreui-toggle="dropdown"]'
const SELECTOR_MENU = '.dropdown-menu'
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'
const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'
const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'
const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'
const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'
const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'
const Default = {
  offset: [0, 2],
  boundary: 'clippingParents',
  reference: 'toggle',
  display: 'dynamic',
  popperConfig: null,
  autoClose: true
}
const DefaultType = {
  offset: '(array|string|function)',
  boundary: '(string|element)',
  reference: '(string|element|object)',
  display: 'string',
  popperConfig: '(null|object|function)',
  autoClose: '(boolean|string)'
}
/**
 * ------------------------------------------------------------------------
 * Class Definition
 * ------------------------------------------------------------------------
 */
class Dropdown extends BaseComponent {
  constructor(element, config) {
    super(element)
    this._popper = null
    this._config = this._getConfig(config)
    this._menu = this._getMenuElement()
    this._inNavbar = this._detectNavbar()
  }
  // Getters
  static get Default() {
    return Default
  }
  static get DefaultType() {
    return DefaultType
  }
  static get NAME() {
    return NAME
  }
  // Public
  toggle() {
    return this._isShown() ? this.hide() : this.show()
  }
  show() {
    if (isDisabled(this._element) || this._isShown(this._menu)) {
      return
    }
    const relatedTarget = {
      relatedTarget: this._element
    }
    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)
    if (showEvent.defaultPrevented) {
      return
    }
    const parent = Dropdown.getParentFromElement(this._element)
    // Totally disable Popper for Dropdowns in Navbar
    if (this._inNavbar) {
      Manipulator.setDataAttribute(this._menu, 'popper', 'none')
    } else {
      this._createPopper(parent)
    }
    // 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 &&
      !parent.closest(SELECTOR_NAVBAR_NAV)) {
      [].concat(...document.body.children)
        .forEach(elem => EventHandler.on(elem, 'mouseover', noop))
    }
    this._element.focus()
    this._element.setAttribute('aria-expanded', true)
    this._menu.classList.add(CLASS_NAME_SHOW)
    this._element.classList.add(CLASS_NAME_SHOW)
    EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)
  }
  hide() {
    if (isDisabled(this._element) || !this._isShown(this._menu)) {
      return
    }
    const relatedTarget = {
      relatedTarget: this._element
    }
    this._completeHide(relatedTarget)
  }
  dispose() {
    if (this._popper) {
      this._popper.destroy()
    }
    super.dispose()
  }
  update() {
    this._inNavbar = this._detectNavbar()
    if (this._popper) {
      this._popper.update()
    }
  }
  // Private
  _completeHide(relatedTarget) {
    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)
    if (hideEvent.defaultPrevented) {
      return
    }
    // If this is a touch-enabled device we remove the extra
    // empty mouseover listeners we added for iOS support
    if ('ontouchstart' in document.documentElement) {
      [].concat(...document.body.children)
        .forEach(elem => EventHandler.off(elem, 'mouseover', noop))
    }
    if (this._popper) {
      this._popper.destroy()
    }
    this._menu.classList.remove(CLASS_NAME_SHOW)
    this._element.classList.remove(CLASS_NAME_SHOW)
    this._element.setAttribute('aria-expanded', 'false')
    Manipulator.removeDataAttribute(this._menu, 'popper')
    EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)
  }
  _getConfig(config) {
    config = {
      ...this.constructor.Default,
      ...Manipulator.getDataAttributes(this._element),
      ...config
    }
    typeCheckConfig(NAME, config, this.constructor.DefaultType)
    if (typeof config.reference === 'object' && !isElement(config.reference) &&
      typeof config.reference.getBoundingClientRect !== 'function'
    ) {
      // Popper virtual elements require a getBoundingClientRect method
      throw new TypeError(`${NAME.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`)
    }
    return config
  }
  _createPopper(parent) {
    if (typeof Popper === 'undefined') {
      throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)')
    }
    let referenceElement = this._element
    if (this._config.reference === 'parent') {
      referenceElement = parent
    } else if (isElement(this._config.reference)) {
      referenceElement = getElement(this._config.reference)
    } else if (typeof this._config.reference === 'object') {
      referenceElement = this._config.reference
    }
    const popperConfig = this._getPopperConfig()
    const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false)
    this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)
    if (isDisplayStatic) {
      Manipulator.setDataAttribute(this._menu, 'popper', 'static')
    }
  }
  _isShown(element = this._element) {
    return element.classList.contains(CLASS_NAME_SHOW)
  }
  _getMenuElement() {
    return SelectorEngine.next(this._element, SELECTOR_MENU)[0]
  }
  _getPlacement() {
    const parentDropdown = this._element.parentNode
    if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
      return PLACEMENT_RIGHT
    }
    if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {
      return PLACEMENT_LEFT
    }
    // We need to trim the value because custom properties can also include spaces
    const isEnd = getComputedStyle(this._menu).getPropertyValue('--cui-position').trim() === 'end'
    if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
      return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP
    }
    return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM
  }
  _detectNavbar() {
    return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null
  }
  _getOffset() {
    const { offset } = this._config
    if (typeof offset === 'string') {
      return offset.split(',').map(val => Number.parseInt(val, 10))
    }
    if (typeof offset === 'function') {
      return popperData => offset(popperData, this._element)
    }
    return offset
  }
  _getPopperConfig() {
    const defaultBsPopperConfig = {
      placement: this._getPlacement(),
      modifiers: [{
        name: 'preventOverflow',
        options: {
          boundary: this._config.boundary
        }
      },
      {
        name: 'offset',
        options: {
          offset: this._getOffset()
        }
      }]
    }
    // Disable Popper if we have a static display
    if (this._config.display === 'static') {
      defaultBsPopperConfig.modifiers = [{
        name: 'applyStyles',
        enabled: false
      }]
    }
    return {
      ...defaultBsPopperConfig,
      ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
    }
  }
  _selectMenuItem({ key, target }) {
    const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)
    if (!items.length) {
      return
    }
    // if target isn't included in items (e.g. when expanding the dropdown)
    // allow cycling to get the last item in case key equals ARROW_UP_KEY
    getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()
  }
  // Static
  static jQueryInterface(config) {
    return this.each(function () {
      const data = Dropdown.getOrCreateInstance(this, config)
      if (typeof config !== 'string') {
        return
      }
      if (typeof data[config] === 'undefined') {
        throw new TypeError(`No method named "${config}"`)
      }
      data[config]()
    })
  }
  static clearMenus(event) {
    if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) {
      return
    }
    const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
    for (let i = 0, len = toggles.length; i < len; i++) {
      const context = Dropdown.getInstance(toggles[i])
      if (!context || context._config.autoClose === false) {
        continue
      }
      if (!context._isShown()) {
        continue
      }
      const relatedTarget = {
        relatedTarget: context._element
      }
      if (event) {
        const composedPath = event.composedPath()
        const isMenuTarget = composedPath.includes(context._menu)
        if (
          composedPath.includes(context._element) ||
          (context._config.autoClose === 'inside' && !isMenuTarget) ||
          (context._config.autoClose === 'outside' && isMenuTarget)
        ) {
          continue
        }
        // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
        if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
          continue
        }
        if (event.type === 'click') {
          relatedTarget.clickEvent = event
        }
      }
      context._completeHide(relatedTarget)
    }
  }
  static getParentFromElement(element) {
    return getElementFromSelector(element) || element.parentNode
  }
  static dataApiKeydownHandler(event) {
    // If not input/textarea:
    //  - And not a key in REGEXP_KEYDOWN => not a dropdown command
    // If input/textarea:
    //  - If space key => not a dropdown command
    //  - If key is other than escape
    //    - If key is not up or down => not a dropdown command
    //    - If trigger inside the menu => not a dropdown command
    if (/input|textarea/i.test(event.target.tagName) ?
      event.key === SPACE_KEY || (event.key !== ESCAPE_KEY &&
      ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) ||
        event.target.closest(SELECTOR_MENU))) :
      !REGEXP_KEYDOWN.test(event.key)) {
      return
    }
    const isActive = this.classList.contains(CLASS_NAME_SHOW)
    if (!isActive && event.key === ESCAPE_KEY) {
      return
    }
    event.preventDefault()
    event.stopPropagation()
    if (isDisabled(this)) {
      return
    }
    const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
    const instance = Dropdown.getOrCreateInstance(getToggleButton)
    if (event.key === ESCAPE_KEY) {
      instance.hide()
      return
    }
    if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {
      if (!isActive) {
        instance.show()
      }
      instance._selectMenuItem(event)
      return
    }
    if (!isActive || event.key === SPACE_KEY) {
      Dropdown.clearMenus()
    }
  }
}
/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)
EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)
EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  event.preventDefault()
  Dropdown.getOrCreateInstance(this).toggle()
})
/**
 * ------------------------------------------------------------------------
 * jQuery
 * ------------------------------------------------------------------------
 * add .Dropdown to jQuery only if jQuery is present
 */
defineJQueryPlugin(Dropdown)
export default Dropdown

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\base-component.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): alert.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's base-component.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */
import Data from './dom/data'
import {
  executeAfterTransition,
  getElement
} from './util/index'
import EventHandler from './dom/event-handler'
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
const VERSION = '4.1.3'
class BaseComponent {
  constructor(element) {
    element = getElement(element)
    if (!element) {
      return
    }
    this._element = element
    Data.set(this._element, this.constructor.DATA_KEY, this)
  }
  dispose() {
    Data.remove(this._element, this.constructor.DATA_KEY)
    EventHandler.off(this._element, this.constructor.EVENT_KEY)
    Object.getOwnPropertyNames(this).forEach(propertyName => {
      this[propertyName] = null
    })
  }
  _queueCallback(callback, element, isAnimated = true) {
    executeAfterTransition(callback, element, isAnimated)
  }
  /** Static */
  static getInstance(element) {
    return Data.get(getElement(element), this.DATA_KEY)
  }
  static getOrCreateInstance(element, config = {}) {
    return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)
  }
  static get VERSION() {
    return VERSION
  }
  static get NAME() {
    throw new Error('You have to implement the static method "NAME", for each component!')
  }
  static get DATA_KEY() {
    return `coreui.${this.NAME}`
  }
  static get EVENT_KEY() {
    return `.${this.DATA_KEY}`
  }
}
export default BaseComponent

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\util\index.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): alert.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's  util/index.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */
const MAX_UID = 1_000_000
const MILLISECONDS_MULTIPLIER = 1000
const TRANSITION_END = 'transitionend'
// Shoutout AngusCroll (https://goo.gl/pxwQGp)
const toType = object => {
  if (object === null || object === undefined) {
    return `${object}`
  }
  return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase()
}
/**
 * --------------------------------------------------------------------------
 * Public Util Api
 * --------------------------------------------------------------------------
 */
const getUID = prefix => {
  do {
    prefix += Math.floor(Math.random() * MAX_UID)
  } while (document.getElementById(prefix))
  return prefix
}
const getSelector = element => {
  let selector = element.getAttribute('data-coreui-target')
  if (!selector || selector === '#') {
    let hrefAttr = element.getAttribute('href')
    // The only valid content that could double as a selector are IDs or classes,
    // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
    // `document.querySelector` will rightfully complain it is invalid.
    // See https://github.com/twbs/bootstrap/issues/32273
    if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {
      return null
    }
    // Just in case some CMS puts out a full URL with the anchor appended
    if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
      hrefAttr = `#${hrefAttr.split('#')[1]}`
    }
    selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null
  }
  return selector
}
const getSelectorFromElement = element => {
  const selector = getSelector(element)
  if (selector) {
    return document.querySelector(selector) ? selector : null
  }
  return null
}
const getElementFromSelector = element => {
  const selector = getSelector(element)
  return selector ? document.querySelector(selector) : null
}
const getTransitionDurationFromElement = element => {
  if (!element) {
    return 0
  }
  // Get transition-duration of the element
  let { transitionDuration, transitionDelay } = window.getComputedStyle(element)
  const floatTransitionDuration = Number.parseFloat(transitionDuration)
  const floatTransitionDelay = Number.parseFloat(transitionDelay)
  // Return 0 if element or transition duration is not found
  if (!floatTransitionDuration && !floatTransitionDelay) {
    return 0
  }
  // If multiple durations are defined, take the first
  transitionDuration = transitionDuration.split(',')[0]
  transitionDelay = transitionDelay.split(',')[0]
  return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER
}
const triggerTransitionEnd = element => {
  element.dispatchEvent(new Event(TRANSITION_END))
}
const isElement = obj => {
  if (!obj || typeof obj !== 'object') {
    return false
  }
  if (typeof obj.jquery !== 'undefined') {
    obj = obj[0]
  }
  return typeof obj.nodeType !== 'undefined'
}
const getElement = obj => {
  if (isElement(obj)) { // it's a jQuery object or a node element
    return obj.jquery ? obj[0] : obj
  }
  if (typeof obj === 'string' && obj.length > 0) {
    return document.querySelector(obj)
  }
  return null
}
const typeCheckConfig = (componentName, config, configTypes) => {
  Object.keys(configTypes).forEach(property => {
    const expectedTypes = configTypes[property]
    const value = config[property]
    const valueType = value && isElement(value) ? 'element' : toType(value)
    if (!new RegExp(expectedTypes).test(valueType)) {
      throw new TypeError(
        `${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`
      )
    }
  })
}
const isVisible = element => {
  if (!isElement(element) || element.getClientRects().length === 0) {
    return false
  }
  const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'
  // Handle `details` element as its content may falsie appear visible when it is closed
  const closedDetails = element.closest('details:not([open])')
  if (!closedDetails) {
    return elementIsVisible
  }
  if (closedDetails !== element) {
    const summary = element.closest('summary')
    if (summary && summary.parentNode !== closedDetails) {
      return false
    }
    if (summary === null) {
      return false
    }
  }
  return elementIsVisible
}
const isDisabled = element => {
  if (!element || element.nodeType !== Node.ELEMENT_NODE) {
    return true
  }
  if (element.classList.contains('disabled')) {
    return true
  }
  if (typeof element.disabled !== 'undefined') {
    return element.disabled
  }
  return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'
}
const findShadowRoot = element => {
  if (!document.documentElement.attachShadow) {
    return null
  }
  // Can find the shadow root otherwise it'll return the document
  if (typeof element.getRootNode === 'function') {
    const root = element.getRootNode()
    return root instanceof ShadowRoot ? root : null
  }
  if (element instanceof ShadowRoot) {
    return element
  }
  // when we don't find a shadow root
  if (!element.parentNode) {
    return null
  }
  return findShadowRoot(element.parentNode)
}
const noop = () => { }
/**
 * Trick to restart an element's animation
 *
 * @param {HTMLElement} element
 * @return void
 *
 * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
 */
const reflow = element => {
  // eslint-disable-next-line no-unused-expressions
  element.offsetHeight
}
const getjQuery = () => {
  const { jQuery } = window
  if (jQuery && !document.body.hasAttribute('data-coreui-no-jquery')) {
    return jQuery
  }
  return null
}
const DOMContentLoadedCallbacks = []
const onDOMContentLoaded = callback => {
  if (document.readyState === 'loading') {
    // add listener on the first call when the document is in loading state
    if (!DOMContentLoadedCallbacks.length) {
      document.addEventListener('DOMContentLoaded', () => {
        DOMContentLoadedCallbacks.forEach(callback => callback())
      })
    }
    DOMContentLoadedCallbacks.push(callback)
  } else {
    callback()
  }
}
const isRTL = () => document.documentElement.dir === 'rtl'
const defineJQueryPlugin = plugin => {
  onDOMContentLoaded(() => {
    const $ = getjQuery()
    /* istanbul ignore if */
    if ($) {
      const name = plugin.NAME
      const JQUERY_NO_CONFLICT = $.fn[name]
      $.fn[name] = plugin.jQueryInterface
      $.fn[name].Constructor = plugin
      $.fn[name].noConflict = () => {
        $.fn[name] = JQUERY_NO_CONFLICT
        return plugin.jQueryInterface
      }
    }
  })
}
const execute = callback => {
  if (typeof callback === 'function') {
    callback()
  }
}
const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
  if (!waitForTransition) {
    execute(callback)
    return
  }
  const durationPadding = 5
  const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding
  let called = false
  const handler = ({ target }) => {
    if (target !== transitionElement) {
      return
    }
    called = true
    transitionElement.removeEventListener(TRANSITION_END, handler)
    execute(callback)
  }
  transitionElement.addEventListener(TRANSITION_END, handler)
  setTimeout(() => {
    if (!called) {
      triggerTransitionEnd(transitionElement)
    }
  }, emulatedDuration)
}
/**
 * Return the previous/next element of a list.
 *
 * @param {array} list    The list of elements
 * @param activeElement   The active element
 * @param shouldGetNext   Choose to get next or previous element
 * @param isCycleAllowed
 * @return {Element|elem} The proper element
 */
const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
  let index = list.indexOf(activeElement)
  // if the element does not exist in the list return an element depending on the direction and if cycle is allowed
  if (index === -1) {
    return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]
  }
  const listLength = list.length
  index += shouldGetNext ? 1 : -1
  if (isCycleAllowed) {
    index = (index + listLength) % listLength
  }
  return list[Math.max(0, Math.min(index, listLength - 1))]
}
export {
  getElement,
  getUID,
  getSelectorFromElement,
  getElementFromSelector,
  getTransitionDurationFromElement,
  triggerTransitionEnd,
  isElement,
  typeCheckConfig,
  isVisible,
  isDisabled,
  findShadowRoot,
  noop,
  getNextActiveElement,
  reflow,
  getjQuery,
  onDOMContentLoaded,
  isRTL,
  defineJQueryPlugin,
  execute,
  executeAfterTransition
}

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dom\data.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dom/data.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's dom/data.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
const elementMap = new Map()
export default {
  set(element, key, instance) {
    if (!elementMap.has(element)) {
      elementMap.set(element, new Map())
    }
    const instanceMap = elementMap.get(element)
    // make it clear we only want one instance per element
    // can be removed later when multiple key/instances are fine to be used
    if (!instanceMap.has(key) && instanceMap.size !== 0) {
      // eslint-disable-next-line no-console
      console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)
      return
    }
    instanceMap.set(key, instance)
  },
  get(element, key) {
    if (elementMap.has(element)) {
      return elementMap.get(element).get(key) || null
    }
    return null
  },
  remove(element, key) {
    if (!elementMap.has(element)) {
      return
    }
    const instanceMap = elementMap.get(element)
    instanceMap.delete(key)
    // free up element references if there are no instances left for an element
    if (instanceMap.size === 0) {
      elementMap.delete(element)
    }
  }
}

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dom\event-handler.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dom/event-handler.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's  dom/event-handler.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */
import { getjQuery } from '../util/index'
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
const namespaceRegex = /[^.]*(?=\..*)\.|.*/
const stripNameRegex = /\..*/
const stripUidRegex = /::\d+$/
const eventRegistry = {} // Events storage
let uidEvent = 1
const customEvents = {
  mouseenter: 'mouseover',
  mouseleave: 'mouseout'
}
const customEventsRegex = /^(mouseenter|mouseleave)/i
const nativeEvents = new Set([
  'click',
  'dblclick',
  'mouseup',
  'mousedown',
  'contextmenu',
  'mousewheel',
  'DOMMouseScroll',
  'mouseover',
  'mouseout',
  'mousemove',
  'selectstart',
  'selectend',
  'keydown',
  'keypress',
  'keyup',
  'orientationchange',
  'touchstart',
  'touchmove',
  'touchend',
  'touchcancel',
  'pointerdown',
  'pointermove',
  'pointerup',
  'pointerleave',
  'pointercancel',
  'gesturestart',
  'gesturechange',
  'gestureend',
  'focus',
  'blur',
  'change',
  'reset',
  'select',
  'submit',
  'focusin',
  'focusout',
  'load',
  'unload',
  'beforeunload',
  'resize',
  'move',
  'DOMContentLoaded',
  'readystatechange',
  'error',
  'abort',
  'scroll'
])
/**
 * ------------------------------------------------------------------------
 * Private methods
 * ------------------------------------------------------------------------
 */
function getUidEvent(element, uid) {
  return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++
}
function getEvent(element) {
  const uid = getUidEvent(element)
  element.uidEvent = uid
  eventRegistry[uid] = eventRegistry[uid] || {}
  return eventRegistry[uid]
}
function bootstrapHandler(element, fn) {
  return function handler(event) {
    event.delegateTarget = element
    if (handler.oneOff) {
      EventHandler.off(element, event.type, fn)
    }
    return fn.apply(element, [event])
  }
}
function bootstrapDelegationHandler(element, selector, fn) {
  return function handler(event) {
    const domElements = element.querySelectorAll(selector)
    for (let { target } = event; target && target !== this; target = target.parentNode) {
      for (let i = domElements.length; i--;) {
        if (domElements[i] === target) {
          event.delegateTarget = target
          if (handler.oneOff) {
            EventHandler.off(element, event.type, selector, fn)
          }
          return fn.apply(target, [event])
        }
      }
    }
    // To please ESLint
    return null
  }
}
function findHandler(events, handler, delegationSelector = null) {
  const uidEventList = Object.keys(events)
  for (let i = 0, len = uidEventList.length; i < len; i++) {
    const event = events[uidEventList[i]]
    if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {
      return event
    }
  }
  return null
}
function normalizeParams(originalTypeEvent, handler, delegationFn) {
  const delegation = typeof handler === 'string'
  const originalHandler = delegation ? delegationFn : handler
  let typeEvent = getTypeEvent(originalTypeEvent)
  const isNative = nativeEvents.has(typeEvent)
  if (!isNative) {
    typeEvent = originalTypeEvent
  }
  return [delegation, originalHandler, typeEvent]
}
function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
  if (typeof originalTypeEvent !== 'string' || !element) {
    return
  }
  if (!handler) {
    handler = delegationFn
    delegationFn = null
  }
  // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
  // this prevents the handler from being dispatched the same way as mouseover or mouseout does
  if (customEventsRegex.test(originalTypeEvent)) {
    const wrapFn = fn => {
      return function (event) {
        if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {
          return fn.call(this, event)
        }
      }
    }
    if (delegationFn) {
      delegationFn = wrapFn(delegationFn)
    } else {
      handler = wrapFn(handler)
    }
  }
  const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
  const events = getEvent(element)
  const handlers = events[typeEvent] || (events[typeEvent] = {})
  const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)
  if (previousFn) {
    previousFn.oneOff = previousFn.oneOff && oneOff
    return
  }
  const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))
  const fn = delegation ?
    bootstrapDelegationHandler(element, handler, delegationFn) :
    bootstrapHandler(element, handler)
  fn.delegationSelector = delegation ? handler : null
  fn.originalHandler = originalHandler
  fn.oneOff = oneOff
  fn.uidEvent = uid
  handlers[uid] = fn
  element.addEventListener(typeEvent, fn, delegation)
}
function removeHandler(element, events, typeEvent, handler, delegationSelector) {
  const fn = findHandler(events[typeEvent], handler, delegationSelector)
  if (!fn) {
    return
  }
  element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))
  delete events[typeEvent][fn.uidEvent]
}
function removeNamespacedHandlers(element, events, typeEvent, namespace) {
  const storeElementEvent = events[typeEvent] || {}
  Object.keys(storeElementEvent).forEach(handlerKey => {
    if (handlerKey.includes(namespace)) {
      const event = storeElementEvent[handlerKey]
      removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
    }
  })
}
function getTypeEvent(event) {
  // allow to get the native events from namespaced events ('click.coreui.button' --> 'click')
  event = event.replace(stripNameRegex, '')
  return customEvents[event] || event
}
const EventHandler = {
  on(element, event, handler, delegationFn) {
    addHandler(element, event, handler, delegationFn, false)
  },
  one(element, event, handler, delegationFn) {
    addHandler(element, event, handler, delegationFn, true)
  },
  off(element, originalTypeEvent, handler, delegationFn) {
    if (typeof originalTypeEvent !== 'string' || !element) {
      return
    }
    const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
    const inNamespace = typeEvent !== originalTypeEvent
    const events = getEvent(element)
    const isNamespace = originalTypeEvent.startsWith('.')
    if (typeof originalHandler !== 'undefined') {
      // Simplest case: handler is passed, remove that listener ONLY.
      if (!events || !events[typeEvent]) {
        return
      }
      removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)
      return
    }
    if (isNamespace) {
      Object.keys(events).forEach(elementEvent => {
        removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))
      })
    }
    const storeElementEvent = events[typeEvent] || {}
    Object.keys(storeElementEvent).forEach(keyHandlers => {
      const handlerKey = keyHandlers.replace(stripUidRegex, '')
      if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
        const event = storeElementEvent[keyHandlers]
        removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
      }
    })
  },
  trigger(element, event, args) {
    if (typeof event !== 'string' || !element) {
      return null
    }
    const $ = getjQuery()
    const typeEvent = getTypeEvent(event)
    const inNamespace = event !== typeEvent
    const isNative = nativeEvents.has(typeEvent)
    let jQueryEvent
    let bubbles = true
    let nativeDispatch = true
    let defaultPrevented = false
    let evt = null
    if (inNamespace && $) {
      jQueryEvent = $.Event(event, args)
      $(element).trigger(jQueryEvent)
      bubbles = !jQueryEvent.isPropagationStopped()
      nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()
      defaultPrevented = jQueryEvent.isDefaultPrevented()
    }
    if (isNative) {
      evt = document.createEvent('HTMLEvents')
      evt.initEvent(typeEvent, bubbles, true)
    } else {
      evt = new CustomEvent(event, {
        bubbles,
        cancelable: true
      })
    }
    // merge custom information in our event
    if (typeof args !== 'undefined') {
      Object.keys(args).forEach(key => {
        Object.defineProperty(evt, key, {
          get() {
            return args[key]
          }
        })
      })
    }
    if (defaultPrevented) {
      evt.preventDefault()
    }
    if (nativeDispatch) {
      element.dispatchEvent(evt)
    }
    if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {
      jQueryEvent.preventDefault()
    }
    return evt
  }
}
export default EventHandler

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dom\manipulator.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dom/manipulator.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's  dom/manipulator.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */

function normalizeData(val) {
  if (val === 'true') {
    return true
  }

  if (val === 'false') {
    return false
  }

  if (val === Number(val).toString()) {
    return Number(val)
  }

  if (val === '' || val === 'null') {
    return null
  }

  return val
}

function normalizeDataKey(key) {
  return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)
}

const Manipulator = {
  setDataAttribute(element, key, value) {
    element.setAttribute(`data-coreui-${normalizeDataKey(key)}`, value)
  },

  removeDataAttribute(element, key) {
    element.removeAttribute(`data-coreui-${normalizeDataKey(key)}`)
  },

  getDataAttributes(element) {
    if (!element) {
      return {}
    }

    const attributes = {}

    Object.keys(element.dataset)
      .filter(key => key.startsWith('coreui'))
      .forEach(key => {
        let pureKey = key.replace(/^coreui/, '')
        pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)
        attributes[pureKey] = normalizeData(element.dataset[key])
      })

    return attributes
  },

  getDataAttribute(element, key) {
    return normalizeData(element.getAttribute(`data-coreui-${normalizeDataKey(key)}`))
  },

  offset(element) {
    const rect = element.getBoundingClientRect()

    return {
      top: rect.top + window.pageYOffset,
      left: rect.left + window.pageXOffset
    }
  },

  position(element) {
    return {
      top: element.offsetTop,
      left: element.offsetLeft
    }
  }
}

export default Manipulator

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dom\selector-engine.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dom/selector-engine.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's  dom/selector-engine.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */

/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */

import { isDisabled, isVisible } from '../util/index'

const NODE_TEXT = 3

const SelectorEngine = {
  find(selector, element = document.documentElement) {
    return [].concat(...Element.prototype.querySelectorAll.call(element, selector))
  },

  findOne(selector, element = document.documentElement) {
    return Element.prototype.querySelector.call(element, selector)
  },

  children(element, selector) {
    return [].concat(...element.children)
      .filter(child => child.matches(selector))
  },

  parents(element, selector) {
    const parents = []

    let ancestor = element.parentNode

    while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {
      if (ancestor.matches(selector)) {
        parents.push(ancestor)
      }

      ancestor = ancestor.parentNode
    }

    return parents
  },

  prev(element, selector) {
    let previous = element.previousElementSibling

    while (previous) {
      if (previous.matches(selector)) {
        return [previous]
      }

      previous = previous.previousElementSibling
    }

    return []
  },

  next(element, selector) {
    let next = element.nextElementSibling

    while (next) {
      if (next.matches(selector)) {
        return [next]
      }

      next = next.nextElementSibling
    }

    return []
  },

  focusableChildren(element) {
    const focusables = [
      'a',
      'button',
      'input',
      'textarea',
      'select',
      'details',
      '[tabindex]',
      '[contenteditable="true"]'
    ].map(selector => `${selector}:not([tabindex^="-"])`).join(', ')

    return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))
  }
}

export default SelectorEngine

```

2 file này phải có để cấu hình

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\index.esm.js\
index.esm.js có thể trống cũng ok

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\index.umd.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): index.esm.js
 * Licensed under MIT (https://coreui.io/license)
 * --------------------------------------------------------------------------
 */
import Dropdown from './src/dropdown'
export default {
  Dropdown
}

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\package.json

```json
{
  "name": "@coreui/coreui",
  "description": "The most popular front-end framework for developing responsive, mobile-first projects on the web rewritten and maintain by the CoreUI Team",
  "version": "4.1.3",
  "config": {
    "version_short": "4.1"
  },
  "keywords": [
    "css",
    "sass",
    "mobile-first",
    "responsive",
    "front-end",
    "framework",
    "web"
  ],
  "homepage": "https://coreui.io",
  "author": "The CoreUI Team (https://github.com/orgs/coreui/people)",
  "scripts": {
    "start": "npm-run-all --parallel watch",
    "js": "npm-run-all js-compile js-minify",
    "js-compile": "npm-run-all --aggregate-output --parallel js-compile-*",
    "js-compile-standalone": "rollup --environment BUNDLE:false --config build/rollup.config.js --sourcemap",
    "js-compile-standalone-esm": "rollup --environment ESM:true,BUNDLE:false --config build/rollup.config.js --sourcemap",
    "js-compile-bundle": "rollup --environment BUNDLE:true --config build/rollup.config.js --sourcemap",
    "watch": "npm-run-all --parallel watch-*",
    "watch-js-main": "nodemon --watch js/src/ --ext js --exec \"npm-run-all js-compile\""
  },
  "style": "dist/css/coreui.css",
  "sass": "scss/coreui.scss",
  "main": "dist/js/coreui.js",
  "module": "dist/js/coreui.esm.js",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/coreui/coreui.git"
  },
  "bugs": {
    "url": "https://github.com/coreui/coreui/issues"
  },
  "license": "MIT",
  "peerDependencies": {
    "@popperjs/core": "^2.11.2"
  },
  "devDependencies": {
    "@babel/cli": "^7.17.6",
    "@babel/core": "^7.17.5",
    "@babel/preset-env": "^7.16.11",
    "@popperjs/core": "^2.11.2",
    "@rollup/plugin-babel": "^5.3.1",
    "@rollup/plugin-commonjs": "^21.0.2",
    "@rollup/plugin-node-resolve": "^13.1.3",
    "@rollup/plugin-replace": "^4.0.0",
    "auto-changelog": "^2.4.0",
    "autoprefixer": "^10.4.2",
    "bundlewatch": "^0.3.3",
    "clean-css-cli": "^5.5.2",
    "cross-env": "^7.0.3",
    "eslint": "^8.10.0",
    "eslint-config-xo": "^0.40.0",
    "eslint-plugin-import": "^2.25.4",
    "eslint-plugin-unicorn": "^41.0.0",
    "find-unused-sass-variables": "^4.0.3",
    "glob": "^7.2.0",
    "globby": "^11.0.4",
    "hammer-simulator": "0.0.1",
    "hugo-bin": "^0.81.1",
    "ip": "^1.1.5",
    "jquery": "^3.6.0",
    "linkinator": "^3.0.3",
    "lockfile-lint": "^4.6.2",
    "nodemon": "^2.0.15",
    "npm-run-all": "^4.1.5",
    "postcss": "^8.4.7",
    "postcss-cli": "^9.1.0",
    "postcss-combine-duplicated-selectors": "^10.0.3",
    "rollup": "^2.69.0",
    "rollup-plugin-istanbul": "^3.0.0",
    "rtlcss": "^3.5.0",
    "sass": "^1.49.9",
    "shelljs": "^0.8.5",
    "stylelint": "^14.5.3",
    "stylelint-config-twbs-bootstrap": "^3.0.1",
    "terser": "^5.12.0",
    "vnu-jar": "21.10.12"
  },
  "files": [
    "dist/{css,js}/*.{css,js,map}",
    "js/{src,dist}/**/*.{js,map}",
    "scss/**/*.scss"
  ],
  "hugo-bin": {
    "buildTags": "extended"
  },
  "jspm": {
    "registry": "npm",
    "main": "js/coreui",
    "directories": {
      "lib": "dist"
    },
    "shim": {
      "js/coreui": {
        "deps": [
          "@popperjs/core"
        ]
      }
    },
    "dependencies": {},
    "peerDependencies": {
      "@popperjs/core": "^2.11.2"
    }
  },
  "dependencies": {
    "@coreui/coreui": "^4.1.3"
  }
}

```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://learncss.gitbook.io/cssadvand/1-giao-dien-su-dung-thuoc-tinh-data-coreui-toggle-dropdown-ok.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
