aboutsummaryrefslogtreecommitdiff
path: root/js/src
diff options
context:
space:
mode:
authorPierre Vanduynslager <[email protected]>2017-04-08 18:43:25 -0400
committerGitHub <[email protected]>2017-04-08 18:43:25 -0400
commitfe72daf2b34263d3cfc9bc77e9998cd22adfa34d (patch)
tree15dc9fc6fcb513362ba112d52ab01b568b423709 /js/src
parentf5cc59145642d78d7abbdf38fee1905786da5367 (diff)
parentfeb35b94a61c4d6016be8d1773a79a6bbe57d856 (diff)
downloadbootstrap-fe72daf2b34263d3cfc9bc77e9998cd22adfa34d.tar.xz
bootstrap-fe72daf2b34263d3cfc9bc77e9998cd22adfa34d.zip
Merge branch 'v4-dev' into dropdown-keyboard
Diffstat (limited to 'js/src')
-rw-r--r--js/src/carousel.js23
-rw-r--r--js/src/collapse.js36
-rw-r--r--js/src/dropdown.js32
-rw-r--r--js/src/modal.js84
-rw-r--r--js/src/scrollspy.js33
-rw-r--r--js/src/tab.js15
-rw-r--r--js/src/tooltip.js50
-rw-r--r--js/src/util.js11
8 files changed, 168 insertions, 116 deletions
diff --git a/js/src/carousel.js b/js/src/carousel.js
index 8a75cb240..7c2da45ad 100644
--- a/js/src/carousel.js
+++ b/js/src/carousel.js
@@ -120,10 +120,9 @@ const Carousel = (($) => {
// public
next() {
- if (this._isSliding) {
- throw new Error('Carousel is sliding')
+ if (!this._isSliding) {
+ this._slide(Direction.NEXT)
}
- this._slide(Direction.NEXT)
}
nextWhenVisible() {
@@ -134,10 +133,9 @@ const Carousel = (($) => {
}
prev() {
- if (this._isSliding) {
- throw new Error('Carousel is sliding')
+ if (!this._isSliding) {
+ this._slide(Direction.PREV)
}
- this._slide(Direction.PREV)
}
pause(event) {
@@ -282,9 +280,13 @@ const Carousel = (($) => {
_triggerSlideEvent(relatedTarget, eventDirectionName) {
+ const targetIndex = this._getItemIndex(relatedTarget)
+ const fromIndex = this._getItemIndex($(this._element).find(Selector.ACTIVE_ITEM)[0])
const slideEvent = $.Event(Event.SLIDE, {
relatedTarget,
- direction: eventDirectionName
+ direction: eventDirectionName,
+ from: fromIndex,
+ to: targetIndex
})
$(this._element).trigger(slideEvent)
@@ -310,9 +312,10 @@ const Carousel = (($) => {
_slide(direction, element) {
const activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]
+ const activeElementIndex = this._getItemIndex(activeElement)
const nextElement = element || activeElement &&
this._getItemByDirection(direction, activeElement)
-
+ const nextElementIndex = this._getItemIndex(nextElement)
const isCycling = Boolean(this._interval)
let directionalClassName
@@ -354,7 +357,9 @@ const Carousel = (($) => {
const slidEvent = $.Event(Event.SLID, {
relatedTarget: nextElement,
- direction: eventDirectionName
+ direction: eventDirectionName,
+ from: activeElementIndex,
+ to: nextElementIndex
})
if (Util.supportsTransitionEnd() &&
diff --git a/js/src/collapse.js b/js/src/collapse.js
index 28c4493cc..88428310d 100644
--- a/js/src/collapse.js
+++ b/js/src/collapse.js
@@ -57,7 +57,8 @@ const Collapse = (($) => {
const Selector = {
ACTIVES : '.card > .show, .card > .collapsing',
- DATA_TOGGLE : '[data-toggle="collapse"]'
+ DATA_TOGGLE : '[data-toggle="collapse"]',
+ DATA_CHILDREN : 'data-children'
}
@@ -77,13 +78,20 @@ const Collapse = (($) => {
`[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]`
))
-
this._parent = this._config.parent ? this._getParent() : null
if (!this._config.parent) {
this._addAriaAndCollapsedClass(this._element, this._triggerArray)
}
+ this._selectorActives = Selector.ACTIVES
+ if (this._parent) {
+ const childrenSelector = this._parent.hasAttribute(Selector.DATA_CHILDREN) ? this._parent.getAttribute(Selector.DATA_CHILDREN) : null
+ if (childrenSelector !== null) {
+ this._selectorActives = `${childrenSelector} > .show, ${childrenSelector} > .collapsing`
+ }
+ }
+
if (this._config.toggle) {
this.toggle()
}
@@ -112,11 +120,8 @@ const Collapse = (($) => {
}
show() {
- if (this._isTransitioning) {
- throw new Error('Collapse is transitioning')
- }
-
- if ($(this._element).hasClass(ClassName.SHOW)) {
+ if (this._isTransitioning ||
+ $(this._element).hasClass(ClassName.SHOW)) {
return
}
@@ -124,7 +129,7 @@ const Collapse = (($) => {
let activesData
if (this._parent) {
- actives = $.makeArray($(this._parent).find(Selector.ACTIVES))
+ actives = $.makeArray($(this._parent).find(this._selectorActives))
if (!actives.length) {
actives = null
}
@@ -196,11 +201,8 @@ const Collapse = (($) => {
}
hide() {
- if (this._isTransitioning) {
- throw new Error('Collapse is transitioning')
- }
-
- if (!$(this._element).hasClass(ClassName.SHOW)) {
+ if (this._isTransitioning ||
+ !$(this._element).hasClass(ClassName.SHOW)) {
return
}
@@ -211,10 +213,8 @@ const Collapse = (($) => {
}
const dimension = this._getDimension()
- const offsetDimension = dimension === Dimension.WIDTH ?
- 'offsetWidth' : 'offsetHeight'
- this._element.style[dimension] = `${this._element[offsetDimension]}px`
+ this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
Util.reflow(this._element)
@@ -357,7 +357,9 @@ const Collapse = (($) => {
*/
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
- event.preventDefault()
+ if (!/input|textarea/i.test(event.target.tagName)) {
+ event.preventDefault()
+ }
const target = Collapse._getTargetFromElement(this)
const data = $(target).data(DATA_KEY)
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 1e85c2530..d7eebd8c9 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -96,16 +96,6 @@ const Dropdown = (($) => {
return false
}
- if ('ontouchstart' in document.documentElement &&
- !$(parent).closest(Selector.NAVBAR_NAV).length) {
-
- // if mobile we use a backdrop because click events don't delegate
- const dropdown = document.createElement('div')
- dropdown.className = ClassName.BACKDROP
- $(dropdown).insertBefore(this)
- $(dropdown).on('click', Dropdown._clearMenus)
- }
-
const relatedTarget = {
relatedTarget : this
}
@@ -117,6 +107,17 @@ const Dropdown = (($) => {
return false
}
+ // set the backdrop only if the dropdown menu will be opened
+ if ('ontouchstart' in document.documentElement &&
+ !$(parent).closest(Selector.NAVBAR_NAV).length) {
+
+ // if mobile we use a backdrop because click events don't delegate
+ const dropdown = document.createElement('div')
+ dropdown.className = ClassName.BACKDROP
+ $(dropdown).insertBefore(this)
+ $(dropdown).on('click', Dropdown._clearMenus)
+ }
+
this.focus()
this.setAttribute('aria-expanded', true)
@@ -166,11 +167,6 @@ const Dropdown = (($) => {
return
}
- const backdrop = $(Selector.BACKDROP)[0]
- if (backdrop) {
- backdrop.parentNode.removeChild(backdrop)
- }
-
const toggles = $.makeArray($(Selector.DATA_TOGGLE))
for (let i = 0; i < toggles.length; i++) {
@@ -195,6 +191,12 @@ const Dropdown = (($) => {
continue
}
+ // remove backdrop only if the dropdown menu will be hidden
+ const backdrop = $(parent).find(Selector.BACKDROP)[0]
+ if (backdrop) {
+ backdrop.parentNode.removeChild(backdrop)
+ }
+
toggles[i].setAttribute('aria-expanded', 'false')
$(parent)
diff --git a/js/src/modal.js b/js/src/modal.js
index 213434f77..779b9a402 100644
--- a/js/src/modal.js
+++ b/js/src/modal.js
@@ -67,7 +67,8 @@ const Modal = (($) => {
DIALOG : '.modal-dialog',
DATA_TOGGLE : '[data-toggle="modal"]',
DATA_DISMISS : '[data-dismiss="modal"]',
- FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
+ FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
+ NAVBAR_TOGGLER : '.navbar-toggler'
}
@@ -87,7 +88,6 @@ const Modal = (($) => {
this._isShown = false
this._isBodyOverflowing = false
this._ignoreBackdropClick = false
- this._isTransitioning = false
this._originalBodyPadding = 0
this._scrollbarWidth = 0
}
@@ -112,13 +112,13 @@ const Modal = (($) => {
show(relatedTarget) {
if (this._isTransitioning) {
- throw new Error('Modal is transitioning')
+ return
}
- if (Util.supportsTransitionEnd() &&
- $(this._element).hasClass(ClassName.FADE)) {
+ if (Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)) {
this._isTransitioning = true
}
+
const showEvent = $.Event(Event.SHOW, {
relatedTarget
})
@@ -161,17 +161,18 @@ const Modal = (($) => {
event.preventDefault()
}
- if (this._isTransitioning) {
- throw new Error('Modal is transitioning')
+ if (this._isTransitioning || !this._isShown) {
+ return
}
- const transition = Util.supportsTransitionEnd() &&
- $(this._element).hasClass(ClassName.FADE)
+ const transition = Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)
+
if (transition) {
this._isTransitioning = true
}
const hideEvent = $.Event(Event.HIDE)
+
$(this._element).trigger(hideEvent)
if (!this._isShown || hideEvent.isDefaultPrevented()) {
@@ -191,6 +192,7 @@ const Modal = (($) => {
$(this._dialog).off(Event.MOUSEDOWN_DISMISS)
if (transition) {
+
$(this._element)
.one(Util.TRANSITION_END, (event) => this._hideModal(event))
.emulateTransitionEnd(TRANSITION_DURATION)
@@ -211,10 +213,12 @@ const Modal = (($) => {
this._isShown = null
this._isBodyOverflowing = null
this._ignoreBackdropClick = null
- this._originalBodyPadding = null
this._scrollbarWidth = null
}
+ handleUpdate() {
+ this._adjustDialog()
+ }
// private
@@ -296,7 +300,7 @@ const Modal = (($) => {
_setResizeEvent() {
if (this._isShown) {
- $(window).on(Event.RESIZE, (event) => this._handleUpdate(event))
+ $(window).on(Event.RESIZE, (event) => this.handleUpdate(event))
} else {
$(window).off(Event.RESIZE)
}
@@ -304,7 +308,7 @@ const Modal = (($) => {
_hideModal() {
this._element.style.display = 'none'
- this._element.setAttribute('aria-hidden', 'true')
+ this._element.setAttribute('aria-hidden', true)
this._isTransitioning = false
this._showBackdrop(() => {
$(document.body).removeClass(ClassName.OPEN)
@@ -401,10 +405,6 @@ const Modal = (($) => {
// todo (fat): these should probably be refactored out of modal.js
// ----------------------------------------------------------------------
- _handleUpdate() {
- this._adjustDialog()
- }
-
_adjustDialog() {
const isModalOverflowing =
this._element.scrollHeight > document.documentElement.clientHeight
@@ -429,28 +429,60 @@ const Modal = (($) => {
}
_setScrollbar() {
- const bodyPadding = parseInt(
- $(Selector.FIXED_CONTENT).css('padding-right') || 0,
- 10
- )
+ if (this._isBodyOverflowing) {
+ // Note: DOMNode.style.paddingRight returns the actual value or '' if not set
+ // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
+
+ // Adjust fixed content padding
+ $(Selector.FIXED_CONTENT).each((index, element) => {
+ const actualPadding = $(element)[0].style.paddingRight
+ const calculatedPadding = $(element).css('padding-right')
+ $(element).data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
+ })
- this._originalBodyPadding = document.body.style.paddingRight || ''
+ // Adjust navbar-toggler margin
+ $(Selector.NAVBAR_TOGGLER).each((index, element) => {
+ const actualMargin = $(element)[0].style.marginRight
+ const calculatedMargin = $(element).css('margin-right')
+ $(element).data('margin-right', actualMargin).css('margin-right', `${parseFloat(calculatedMargin) + this._scrollbarWidth}px`)
+ })
- if (this._isBodyOverflowing) {
- document.body.style.paddingRight =
- `${bodyPadding + this._scrollbarWidth}px`
+ // Adjust body padding
+ const actualPadding = document.body.style.paddingRight
+ const calculatedPadding = $('body').css('padding-right')
+ $('body').data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
}
}
_resetScrollbar() {
- document.body.style.paddingRight = this._originalBodyPadding
+ // Restore fixed content padding
+ $(Selector.FIXED_CONTENT).each((index, element) => {
+ const padding = $(element).data('padding-right')
+ if (typeof padding !== 'undefined') {
+ $(element).css('padding-right', padding).removeData('padding-right')
+ }
+ })
+
+ // Restore navbar-toggler margin
+ $(Selector.NAVBAR_TOGGLER).each((index, element) => {
+ const margin = $(element).data('margin-right')
+ if (typeof margin !== 'undefined') {
+ $(element).css('margin-right', margin).removeData('margin-right')
+ }
+ })
+
+ // Restore body padding
+ const padding = $('body').data('padding-right')
+ if (typeof padding !== 'undefined') {
+ $('body').css('padding-right', padding).removeData('padding-right')
+ }
}
_getScrollbarWidth() { // thx d.walsh
const scrollDiv = document.createElement('div')
scrollDiv.className = ClassName.SCROLLBAR_MEASURER
document.body.appendChild(scrollDiv)
- const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
+ const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
document.body.removeChild(scrollDiv)
return scrollbarWidth
}
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js
index 66b6080c8..7ea9f2483 100644
--- a/js/src/scrollspy.js
+++ b/js/src/scrollspy.js
@@ -45,18 +45,15 @@ const ScrollSpy = (($) => {
const ClassName = {
DROPDOWN_ITEM : 'dropdown-item',
DROPDOWN_MENU : 'dropdown-menu',
- NAV_LINK : 'nav-link',
- NAV : 'nav',
ACTIVE : 'active'
}
const Selector = {
DATA_SPY : '[data-spy="scroll"]',
ACTIVE : '.active',
- LIST_ITEM : '.list-item',
- LI : 'li',
- LI_DROPDOWN : 'li.dropdown',
+ NAV_LIST_GROUP : '.nav, .list-group',
NAV_LINKS : '.nav-link',
+ LIST_ITEMS : '.list-group-item',
DROPDOWN : '.dropdown',
DROPDOWN_ITEMS : '.dropdown-item',
DROPDOWN_TOGGLE : '.dropdown-toggle'
@@ -81,6 +78,7 @@ const ScrollSpy = (($) => {
this._scrollElement = element.tagName === 'BODY' ? window : element
this._config = this._getConfig(config)
this._selector = `${this._config.target} ${Selector.NAV_LINKS},`
+ + `${this._config.target} ${Selector.LIST_ITEMS},`
+ `${this._config.target} ${Selector.DROPDOWN_ITEMS}`
this._offsets = []
this._targets = []
@@ -133,12 +131,15 @@ const ScrollSpy = (($) => {
target = $(targetSelector)[0]
}
- if (target && (target.offsetWidth || target.offsetHeight)) {
- // todo (fat): remove sketch reliance on jQuery position/offset
- return [
- $(target)[offsetMethod]().top + offsetBase,
- targetSelector
- ]
+ if (target) {
+ const targetBCR = target.getBoundingClientRect()
+ if (targetBCR.width || targetBCR.height) {
+ // todo (fat): remove sketch reliance on jQuery position/offset
+ return [
+ $(target)[offsetMethod]().top + offsetBase,
+ targetSelector
+ ]
+ }
}
return null
})
@@ -198,7 +199,7 @@ const ScrollSpy = (($) => {
_getOffsetHeight() {
return this._scrollElement === window ?
- window.innerHeight : this._scrollElement.offsetHeight
+ window.innerHeight : this._scrollElement.getBoundingClientRect().height
}
_process() {
@@ -256,9 +257,11 @@ const ScrollSpy = (($) => {
$link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
$link.addClass(ClassName.ACTIVE)
} else {
- // todo (fat) this is kinda sus...
- // recursively add actives to tested nav-links
- $link.parents(Selector.LI).find(`> ${Selector.NAV_LINKS}`).addClass(ClassName.ACTIVE)
+ // Set triggered link as active
+ $link.addClass(ClassName.ACTIVE)
+ // Set triggered links parents as active
+ // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
+ $link.parents(Selector.NAV_LIST_GROUP).prev(`${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`).addClass(ClassName.ACTIVE)
}
$(this._scrollElement).trigger(Event.ACTIVATE, {
diff --git a/js/src/tab.js b/js/src/tab.js
index c069b0a9a..6f8187d17 100644
--- a/js/src/tab.js
+++ b/js/src/tab.js
@@ -42,14 +42,10 @@ const Tab = (($) => {
}
const Selector = {
- A : 'a',
- LI : 'li',
DROPDOWN : '.dropdown',
- LIST : 'ul:not(.dropdown-menu), ol:not(.dropdown-menu), nav:not(.dropdown-menu)',
- FADE_CHILD : '> .nav-item .fade, > .fade',
+ NAV_LIST_GROUP : '.nav, .list-group',
ACTIVE : '.active',
- ACTIVE_CHILD : '> .nav-item > .active, > .active',
- DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"]',
+ DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
DROPDOWN_TOGGLE : '.dropdown-toggle',
DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
}
@@ -87,7 +83,7 @@ const Tab = (($) => {
let target
let previous
- const listElement = $(this._element).closest(Selector.LIST)[0]
+ const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0]
const selector = Util.getSelectorFromElement(this._element)
if (listElement) {
@@ -152,11 +148,10 @@ const Tab = (($) => {
// private
_activate(element, container, callback) {
- const active = $(container).find(Selector.ACTIVE_CHILD)[0]
+ const active = $(container).find(Selector.ACTIVE)[0]
const isTransitioning = callback
&& Util.supportsTransitionEnd()
- && (active && $(active).hasClass(ClassName.FADE)
- || Boolean($(container).find(Selector.FADE_CHILD)[0]))
+ && (active && $(active).hasClass(ClassName.FADE))
const complete = () => this._transitionComplete(
element,
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index e750dcecc..1ff2c4f6e 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -34,6 +34,7 @@ const Tooltip = (($) => {
const JQUERY_NO_CONFLICT = $.fn[NAME]
const TRANSITION_DURATION = 150
const CLASS_PREFIX = 'bs-tether'
+ const TETHER_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
const Default = {
animation : true,
@@ -123,12 +124,11 @@ const Tooltip = (($) => {
constructor(element, config) {
// private
- this._isEnabled = true
- this._timeout = 0
- this._hoverState = ''
- this._activeTrigger = {}
- this._isTransitioning = false
- this._tether = null
+ this._isEnabled = true
+ this._timeout = 0
+ this._hoverState = ''
+ this._activeTrigger = {}
+ this._tether = null
// protected
this.element = element
@@ -249,9 +249,6 @@ const Tooltip = (($) => {
const showEvent = $.Event(this.constructor.Event.SHOW)
if (this.isWithContent() && this._isEnabled) {
- if (this._isTransitioning) {
- throw new Error('Tooltip is transitioning')
- }
$(this.element).trigger(showEvent)
const isInTheDom = $.contains(
@@ -283,9 +280,11 @@ const Tooltip = (($) => {
const container = this.config.container === false ? document.body : $(this.config.container)
- $(tip)
- .data(this.constructor.DATA_KEY, this)
- .appendTo(container)
+ $(tip).data(this.constructor.DATA_KEY, this)
+
+ if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
+ $(tip).appendTo(container)
+ }
$(this.element).trigger(this.constructor.Event.INSERTED)
@@ -307,8 +306,7 @@ const Tooltip = (($) => {
const complete = () => {
const prevHoverState = this._hoverState
- this._hoverState = null
- this._isTransitioning = false
+ this._hoverState = null
$(this.element).trigger(this.constructor.Event.SHOWN)
@@ -318,7 +316,6 @@ const Tooltip = (($) => {
}
if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {
- this._isTransitioning = true
$(this.tip)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
@@ -332,17 +329,14 @@ const Tooltip = (($) => {
hide(callback) {
const tip = this.getTipElement()
const hideEvent = $.Event(this.constructor.Event.HIDE)
- if (this._isTransitioning) {
- throw new Error('Tooltip is transitioning')
- }
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)
- this._isTransitioning = false
this.cleanupTether()
if (callback) {
@@ -364,7 +358,7 @@ const Tooltip = (($) => {
if (Util.supportsTransitionEnd() &&
$(this.tip).hasClass(ClassName.FADE)) {
- this._isTransitioning = true
+
$(tip)
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(TRANSITION_DURATION)
@@ -438,6 +432,14 @@ const Tooltip = (($) => {
return AttachmentMap[placement.toUpperCase()]
}
+ _cleanTipClass() {
+ const $tip = $(this.getTipElement())
+ const tabClass = $tip.attr('class').match(TETHER_PREFIX_REGEX)
+ if (tabClass !== null && tabClass.length > 0) {
+ $tip.removeClass(tabClass.join(''))
+ }
+ }
+
_setListeners() {
const triggers = this.config.trigger.split(' ')
@@ -603,6 +605,14 @@ const Tooltip = (($) => {
}
}
+ if (config.title && typeof config.title === 'number') {
+ config.title = config.title.toString()
+ }
+
+ if (config.content && typeof config.content === 'number') {
+ config.content = config.content.toString()
+ }
+
Util.typeCheckConfig(
NAME,
config,
diff --git a/js/src/util.js b/js/src/util.js
index 515eba6d9..3c0d02251 100644
--- a/js/src/util.js
+++ b/js/src/util.js
@@ -112,13 +112,16 @@ const Util = (($) => {
getSelectorFromElement(element) {
let selector = element.getAttribute('data-target')
-
- if (!selector) {
+ if (!selector || selector === '#') {
selector = element.getAttribute('href') || ''
- selector = /^#[a-z]/i.test(selector) ? selector : null
}
- return selector
+ try {
+ const $selector = $(selector)
+ return $selector.length > 0 ? selector : null
+ } catch (error) {
+ return null
+ }
},
reflow(element) {