Skip to content

Commit

Permalink
Move getElementFromSelector & getSelectorFromElement to SelectorE…
Browse files Browse the repository at this point in the history
…ngine (#36027)

* Move `getElementFromSelector` & getSelectorFromElement` inside selector-engine.js, in order to use SelectorEngine methods, avoiding raw querySelector usage

* add `getMultipleElementsFromSelector` helper

Co-authored-by: Julien Déramond <[email protected]>
  • Loading branch information
GeoSot and julien-deramond authored Nov 6, 2022
1 parent 2b21094 commit e81e7cd
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 179 deletions.
3 changes: 1 addition & 2 deletions js/src/carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import {
defineJQueryPlugin,
getElementFromSelector,
getNextActiveElement,
isRTL,
isVisible,
Expand Down Expand Up @@ -431,7 +430,7 @@ class Carousel extends BaseComponent {
*/

EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {
const target = getElementFromSelector(this)
const target = SelectorEngine.getElementFromSelector(this)

if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
return
Expand Down
13 changes: 4 additions & 9 deletions js/src/collapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import {
defineJQueryPlugin,
getElement,
getElementFromSelector,
getSelectorFromElement,
reflow
} from './util/index.js'
import EventHandler from './dom/event-handler.js'
Expand Down Expand Up @@ -68,7 +66,7 @@ class Collapse extends BaseComponent {
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)

for (const elem of toggleList) {
const selector = getSelectorFromElement(elem)
const selector = SelectorEngine.getSelectorFromElement(elem)
const filterElement = SelectorEngine.find(selector)
.filter(foundElement => foundElement === this._element)

Expand Down Expand Up @@ -185,7 +183,7 @@ class Collapse extends BaseComponent {
this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)

for (const trigger of this._triggerArray) {
const element = getElementFromSelector(trigger)
const element = SelectorEngine.getElementFromSelector(trigger)

if (element && !this._isShown(element)) {
this._addAriaAndCollapsedClass([trigger], false)
Expand Down Expand Up @@ -229,7 +227,7 @@ class Collapse extends BaseComponent {
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)

for (const element of children) {
const selected = getElementFromSelector(element)
const selected = SelectorEngine.getElementFromSelector(element)

if (selected) {
this._addAriaAndCollapsedClass([element], this._isShown(selected))
Expand Down Expand Up @@ -285,10 +283,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
event.preventDefault()
}

const selector = getSelectorFromElement(this)
const selectorElements = SelectorEngine.find(selector)

for (const element of selectorElements) {
for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {
Collapse.getOrCreateInstance(element, { toggle: false }).toggle()
}
})
Expand Down
47 changes: 47 additions & 0 deletions js/src/dom/selector-engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,53 @@ const SelectorEngine = {
].map(selector => `${selector}:not([tabindex^="-"])`).join(',')

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

getSelector(element) {
let selector = element.getAttribute('data-bs-target')

if (!selector || selector === '#') {
let hrefAttribute = 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 (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {
return null
}

// Just in case some CMS puts out a full URL with the anchor appended
if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
hrefAttribute = `#${hrefAttribute.split('#')[1]}`
}

selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
}

return selector
},

getSelectorFromElement(element) {
const selector = SelectorEngine.getSelector(element)

if (selector) {
return SelectorEngine.findOne(selector) ? selector : null
}

return null
},

getElementFromSelector(element) {
const selector = SelectorEngine.getSelector(element)

return selector ? SelectorEngine.findOne(selector) : null
},

getMultipleElementsFromSelector(element) {
const selector = SelectorEngine.getSelector(element)

return selector ? SelectorEngine.find(selector) : []
}
}

Expand Down
4 changes: 2 additions & 2 deletions js/src/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* --------------------------------------------------------------------------
*/

import { defineJQueryPlugin, getElementFromSelector, isRTL, isVisible, reflow } from './util/index.js'
import { defineJQueryPlugin, isRTL, isVisible, reflow } from './util/index.js'
import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js'
import ScrollBarHelper from './util/scrollbar.js'
Expand Down Expand Up @@ -336,7 +336,7 @@ class Modal extends BaseComponent {
*/

EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
const target = getElementFromSelector(this)
const target = SelectorEngine.getElementFromSelector(this)

if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
Expand Down
3 changes: 1 addition & 2 deletions js/src/offcanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import {
defineJQueryPlugin,
getElementFromSelector,
isDisabled,
isVisible
} from './util/index.js'
Expand Down Expand Up @@ -231,7 +230,7 @@ class Offcanvas extends BaseComponent {
*/

EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
const target = getElementFromSelector(this)
const target = SelectorEngine.getElementFromSelector(this)

if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
Expand Down
8 changes: 4 additions & 4 deletions js/src/tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* --------------------------------------------------------------------------
*/

import { defineJQueryPlugin, getElementFromSelector, getNextActiveElement, isDisabled } from './util/index.js'
import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'
import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js'
import BaseComponent from './base-component.js'
Expand Down Expand Up @@ -106,7 +106,7 @@ class Tab extends BaseComponent {

element.classList.add(CLASS_NAME_ACTIVE)

this._activate(getElementFromSelector(element)) // Search and activate/show the proper section
this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section

const complete = () => {
if (element.getAttribute('role') !== 'tab') {
Expand All @@ -133,7 +133,7 @@ class Tab extends BaseComponent {
element.classList.remove(CLASS_NAME_ACTIVE)
element.blur()

this._deactivate(getElementFromSelector(element)) // Search and deactivate the shown section too
this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too

const complete = () => {
if (element.getAttribute('role') !== 'tab') {
Expand Down Expand Up @@ -203,7 +203,7 @@ class Tab extends BaseComponent {
}

_setInitialAttributesOnTargetPanel(child) {
const target = getElementFromSelector(child)
const target = SelectorEngine.getElementFromSelector(child)

if (!target) {
return
Expand Down
5 changes: 3 additions & 2 deletions js/src/util/component-functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/

import EventHandler from '../dom/event-handler.js'
import { getElementFromSelector, isDisabled } from './index.js'
import { isDisabled } from './index.js'
import SelectorEngine from '../dom/selector-engine.js'

const enableDismissTrigger = (component, method = 'hide') => {
const clickEvent = `click.dismiss${component.EVENT_KEY}`
Expand All @@ -21,7 +22,7 @@ const enableDismissTrigger = (component, method = 'hide') => {
return
}

const target = getElementFromSelector(this) || this.closest(`.${name}`)
const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)
const instance = component.getOrCreateInstance(target)

// Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
Expand Down
43 changes: 0 additions & 43 deletions js/src/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,47 +30,6 @@ const getUID = prefix => {
return prefix
}

const getSelector = element => {
let selector = element.getAttribute('data-bs-target')

if (!selector || selector === '#') {
let hrefAttribute = 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 (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {
return null
}

// Just in case some CMS puts out a full URL with the anchor appended
if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
hrefAttribute = `#${hrefAttribute.split('#')[1]}`
}

selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.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
Expand Down Expand Up @@ -316,10 +275,8 @@ export {
executeAfterTransition,
findShadowRoot,
getElement,
getElementFromSelector,
getjQuery,
getNextActiveElement,
getSelectorFromElement,
getTransitionDurationFromElement,
getUID,
isDisabled,
Expand Down
Loading

0 comments on commit e81e7cd

Please sign in to comment.