Skip to content

Commit 48a95f7

Browse files
alpadevXhmikosRrohit2sharma95
authored
refactor: use a Map instead of an Object in dom/data (#32180)
Co-authored-by: XhmikosR <[email protected]> Co-authored-by: Rohit Sharma <[email protected]>
1 parent 6d93a13 commit 48a95f7

15 files changed

+126
-161
lines changed

.bundlewatch.config.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
},
3939
{
4040
"path": "./dist/js/bootstrap.bundle.min.js",
41-
"maxSize": "21.75 kB"
41+
"maxSize": "22 kB"
4242
},
4343
{
4444
"path": "./dist/js/bootstrap.esm.js",

js/src/alert.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class Alert extends BaseComponent {
9898

9999
static jQueryInterface(config) {
100100
return this.each(function () {
101-
let data = Data.getData(this, DATA_KEY)
101+
let data = Data.get(this, DATA_KEY)
102102

103103
if (!data) {
104104
data = new Alert(this)

js/src/base-component.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ class BaseComponent {
2424
}
2525

2626
this._element = element
27-
Data.setData(this._element, this.constructor.DATA_KEY, this)
27+
Data.set(this._element, this.constructor.DATA_KEY, this)
2828
}
2929

3030
dispose() {
31-
Data.removeData(this._element, this.constructor.DATA_KEY)
31+
Data.remove(this._element, this.constructor.DATA_KEY)
3232
this._element = null
3333
}
3434

3535
/** Static */
3636

3737
static getInstance(element) {
38-
return Data.getData(element, this.DATA_KEY)
38+
return Data.get(element, this.DATA_KEY)
3939
}
4040

4141
static get VERSION() {

js/src/button.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class Button extends BaseComponent {
5151

5252
static jQueryInterface(config) {
5353
return this.each(function () {
54-
let data = Data.getData(this, DATA_KEY)
54+
let data = Data.get(this, DATA_KEY)
5555

5656
if (!data) {
5757
data = new Button(this)
@@ -75,7 +75,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
7575

7676
const button = event.target.closest(SELECTOR_DATA_TOGGLE)
7777

78-
let data = Data.getData(button, DATA_KEY)
78+
let data = Data.get(button, DATA_KEY)
7979
if (!data) {
8080
data = new Button(button)
8181
}

js/src/carousel.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ class Carousel extends BaseComponent {
527527
// Static
528528

529529
static carouselInterface(element, config) {
530-
let data = Data.getData(element, DATA_KEY)
530+
let data = Data.get(element, DATA_KEY)
531531
let _config = {
532532
...Default,
533533
...Manipulator.getDataAttributes(element)
@@ -586,7 +586,7 @@ class Carousel extends BaseComponent {
586586
Carousel.carouselInterface(target, config)
587587

588588
if (slideIndex) {
589-
Data.getData(target, DATA_KEY).to(slideIndex)
589+
Data.get(target, DATA_KEY).to(slideIndex)
590590
}
591591

592592
event.preventDefault()
@@ -605,7 +605,7 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
605605
const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)
606606

607607
for (let i = 0, len = carousels.length; i < len; i++) {
608-
Carousel.carouselInterface(carousels[i], Data.getData(carousels[i], DATA_KEY))
608+
Carousel.carouselInterface(carousels[i], Data.get(carousels[i], DATA_KEY))
609609
}
610610
})
611611

js/src/collapse.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class Collapse extends BaseComponent {
147147
const container = SelectorEngine.findOne(this._selector)
148148
if (actives) {
149149
const tempActiveData = actives.find(elem => container !== elem)
150-
activesData = tempActiveData ? Data.getData(tempActiveData, DATA_KEY) : null
150+
activesData = tempActiveData ? Data.get(tempActiveData, DATA_KEY) : null
151151

152152
if (activesData && activesData._isTransitioning) {
153153
return
@@ -166,7 +166,7 @@ class Collapse extends BaseComponent {
166166
}
167167

168168
if (!activesData) {
169-
Data.setData(elemActive, DATA_KEY, null)
169+
Data.set(elemActive, DATA_KEY, null)
170170
}
171171
})
172172
}
@@ -332,7 +332,7 @@ class Collapse extends BaseComponent {
332332
// Static
333333

334334
static collapseInterface(element, config) {
335-
let data = Data.getData(element, DATA_KEY)
335+
let data = Data.get(element, DATA_KEY)
336336
const _config = {
337337
...Default,
338338
...Manipulator.getDataAttributes(element),
@@ -380,7 +380,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
380380
const selectorElements = SelectorEngine.find(selector)
381381

382382
selectorElements.forEach(element => {
383-
const data = Data.getData(element, DATA_KEY)
383+
const data = Data.get(element, DATA_KEY)
384384
let config
385385
if (data) {
386386
// update parent attribute

js/src/dom/data.js

+35-45
Original file line numberDiff line numberDiff line change
@@ -11,57 +11,47 @@
1111
* ------------------------------------------------------------------------
1212
*/
1313

14-
const mapData = (() => {
15-
const storeData = {}
16-
let id = 1
17-
return {
18-
set(element, key, data) {
19-
if (typeof element.bsKey === 'undefined') {
20-
element.bsKey = {
21-
key,
22-
id
23-
}
24-
id++
25-
}
14+
const elementMap = new Map()
2615

27-
storeData[element.bsKey.id] = data
28-
},
29-
get(element, key) {
30-
if (!element || typeof element.bsKey === 'undefined') {
31-
return null
32-
}
33-
34-
const keyProperties = element.bsKey
35-
if (keyProperties.key === key) {
36-
return storeData[keyProperties.id]
37-
}
16+
export default {
17+
set(element, key, instance) {
18+
if (!elementMap.has(element)) {
19+
elementMap.set(element, new Map())
20+
}
3821

39-
return null
40-
},
41-
delete(element, key) {
42-
if (typeof element.bsKey === 'undefined') {
43-
return
44-
}
22+
const instanceMap = elementMap.get(element)
4523

46-
const keyProperties = element.bsKey
47-
if (keyProperties.key === key) {
48-
delete storeData[keyProperties.id]
49-
delete element.bsKey
50-
}
24+
// make it clear we only want one instance per element
25+
// can be removed later when multiple key/instances are fine to be used
26+
if (!instanceMap.has(key) && instanceMap.size !== 0) {
27+
// eslint-disable-next-line no-console
28+
console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)
29+
return
5130
}
52-
}
53-
})()
5431

55-
const Data = {
56-
setData(instance, key, data) {
57-
mapData.set(instance, key, data)
32+
instanceMap.set(key, instance)
5833
},
59-
getData(instance, key) {
60-
return mapData.get(instance, key)
34+
35+
get(element, key) {
36+
if (elementMap.has(element)) {
37+
return elementMap.get(element).get(key) || null
38+
}
39+
40+
return null
6141
},
62-
removeData(instance, key) {
63-
mapData.delete(instance, key)
42+
43+
remove(element, key) {
44+
if (!elementMap.has(element)) {
45+
return
46+
}
47+
48+
const instanceMap = elementMap.get(element)
49+
50+
instanceMap.delete(key)
51+
52+
// free up element references if there are no instances left for an element
53+
if (instanceMap.size === 0) {
54+
elementMap.delete(element)
55+
}
6456
}
6557
}
66-
67-
export default Data

js/src/dropdown.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ class Dropdown extends BaseComponent {
357357
// Static
358358

359359
static dropdownInterface(element, config) {
360-
let data = Data.getData(element, DATA_KEY)
360+
let data = Data.get(element, DATA_KEY)
361361
const _config = typeof config === 'object' ? config : null
362362

363363
if (!data) {
@@ -387,7 +387,7 @@ class Dropdown extends BaseComponent {
387387
const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
388388

389389
for (let i = 0, len = toggles.length; i < len; i++) {
390-
const context = Data.getData(toggles[i], DATA_KEY)
390+
const context = Data.get(toggles[i], DATA_KEY)
391391
const relatedTarget = {
392392
relatedTarget: toggles[i]
393393
}

js/src/modal.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ class Modal extends BaseComponent {
508508

509509
static jQueryInterface(config, relatedTarget) {
510510
return this.each(function () {
511-
let data = Data.getData(this, DATA_KEY)
511+
let data = Data.get(this, DATA_KEY)
512512
const _config = {
513513
...Default,
514514
...Manipulator.getDataAttributes(this),
@@ -556,7 +556,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
556556
})
557557
})
558558

559-
let data = Data.getData(target, DATA_KEY)
559+
let data = Data.get(target, DATA_KEY)
560560
if (!data) {
561561
const config = {
562562
...Manipulator.getDataAttributes(target),

js/src/popover.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class Popover extends Tooltip {
136136

137137
static jQueryInterface(config) {
138138
return this.each(function () {
139-
let data = Data.getData(this, DATA_KEY)
139+
let data = Data.get(this, DATA_KEY)
140140
const _config = typeof config === 'object' ? config : null
141141

142142
if (!data && /dispose|hide/.test(config)) {
@@ -145,7 +145,7 @@ class Popover extends Tooltip {
145145

146146
if (!data) {
147147
data = new Popover(this, _config)
148-
Data.setData(this, DATA_KEY, data)
148+
Data.set(this, DATA_KEY, data)
149149
}
150150

151151
if (typeof config === 'string') {

js/src/scrollspy.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ class ScrollSpy extends BaseComponent {
278278

279279
static jQueryInterface(config) {
280280
return this.each(function () {
281-
let data = Data.getData(this, DATA_KEY)
281+
let data = Data.get(this, DATA_KEY)
282282
const _config = typeof config === 'object' && config
283283

284284
if (!data) {

js/src/tab.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class Tab extends BaseComponent {
182182

183183
static jQueryInterface(config) {
184184
return this.each(function () {
185-
const data = Data.getData(this, DATA_KEY) || new Tab(this)
185+
const data = Data.get(this, DATA_KEY) || new Tab(this)
186186

187187
if (typeof config === 'string') {
188188
if (typeof data[config] === 'undefined') {
@@ -204,7 +204,7 @@ class Tab extends BaseComponent {
204204
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
205205
event.preventDefault()
206206

207-
const data = Data.getData(this, DATA_KEY) || new Tab(this)
207+
const data = Data.get(this, DATA_KEY) || new Tab(this)
208208
data.show()
209209
})
210210

js/src/toast.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ class Toast extends BaseComponent {
189189

190190
static jQueryInterface(config) {
191191
return this.each(function () {
192-
let data = Data.getData(this, DATA_KEY)
192+
let data = Data.get(this, DATA_KEY)
193193
const _config = typeof config === 'object' && config
194194

195195
if (!data) {

js/src/tooltip.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ class Tooltip extends BaseComponent {
275275
this._addAttachmentClass(attachment)
276276

277277
const container = this._getContainer()
278-
Data.setData(tip, this.constructor.DATA_KEY, this)
278+
Data.set(tip, this.constructor.DATA_KEY, this)
279279

280280
if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
281281
container.appendChild(tip)
@@ -465,11 +465,11 @@ class Tooltip extends BaseComponent {
465465

466466
_initializeOnDelegatedTarget(event, context) {
467467
const dataKey = this.constructor.DATA_KEY
468-
context = context || Data.getData(event.delegateTarget, dataKey)
468+
context = context || Data.get(event.delegateTarget, dataKey)
469469

470470
if (!context) {
471471
context = new this.constructor(event.delegateTarget, this._getDelegateConfig())
472-
Data.setData(event.delegateTarget, dataKey, context)
472+
Data.set(event.delegateTarget, dataKey, context)
473473
}
474474

475475
return context
@@ -761,7 +761,7 @@ class Tooltip extends BaseComponent {
761761

762762
static jQueryInterface(config) {
763763
return this.each(function () {
764-
let data = Data.getData(this, DATA_KEY)
764+
let data = Data.get(this, DATA_KEY)
765765
const _config = typeof config === 'object' && config
766766

767767
if (!data && /dispose|hide/.test(config)) {

0 commit comments

Comments
 (0)