Skip to content

Commit 1ea46f9

Browse files
tkirda-bisonclaude
andcommitted
perf: cache jQuery wrappers for the two persistent containers
Phase 3/3 of the optimization pass. Runtime perf cleanup; specs still pass. - Add this.$container and this.$noSuggestionsContainer fields, set once during initialize(). Replace ~12 call sites that re-invoked $(this.suggestionsContainer) / $(this.noSuggestionsContainer) every time they needed the wrapper. - noSuggestions(): chain empty().append(...) since they're now both on the cached wrapper. - adjustContainerWidth(): drop the local `container` variable since both branches just hit this.$container directly. Bundle size flat (12943 -> 12960). The field declarations and assignments offset the call-site savings byte-for-byte. The win is runtime: each hot path (suggest, fixPosition, adjustScroll, hide, activate) no longer creates a fresh jQuery wrapper on every call. Brief experiment with target: es2022 in scripts/build.mjs revealed almost no size win, so reverted to es2020 for wider evergreen coverage. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 22ee4aa commit 1ea46f9

5 files changed

Lines changed: 77 additions & 78 deletions

File tree

dist/Autocomplete.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export declare class Autocomplete {
2020
isLocal: boolean;
2121
suggestionsContainer: HTMLDivElement;
2222
noSuggestionsContainer: HTMLElement;
23+
$container: JQuery;
24+
$noSuggestionsContainer: JQuery;
2325
options: ResolvedOptions;
2426
classes: Classes;
2527
hint: Suggestion | null;

dist/jquery.autocomplete.esm.js

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,17 @@ var _Autocomplete = class _Autocomplete {
121121
const selected = this.classes.selected;
122122
const options = this.options;
123123
this.element.setAttribute("autocomplete", "off");
124-
this.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>').html(options.noSuggestionNotice).get(0);
124+
this.$noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>').html(
125+
options.noSuggestionNotice
126+
);
127+
this.noSuggestionsContainer = this.$noSuggestionsContainer.get(0);
125128
this.suggestionsContainer = _Autocomplete.utils.createNode(options.containerClass);
126-
const container = $(this.suggestionsContainer);
127-
container.appendTo(options.appendTo || "body");
129+
this.$container = $(this.suggestionsContainer);
130+
this.$container.appendTo(options.appendTo || "body");
128131
if (options.width !== "auto") {
129-
container.css("width", options.width);
132+
this.$container.css("width", options.width);
130133
}
134+
const container = this.$container;
131135
container.on("mouseover.autocomplete", suggestionSelector, function() {
132136
self.activate($(this).data("index"));
133137
});
@@ -188,7 +192,7 @@ var _Autocomplete = class _Autocomplete {
188192
options.lookup = this.verifySuggestionsFormat(options.lookup);
189193
}
190194
options.orientation = this.validateOrientation(options.orientation, "bottom");
191-
$(this.suggestionsContainer).css({
195+
this.$container.css({
192196
"max-height": `${options.maxHeight}px`,
193197
width: `${options.width}px`,
194198
"z-index": options.zIndex
@@ -215,7 +219,7 @@ var _Autocomplete = class _Autocomplete {
215219
this.disabled = false;
216220
}
217221
fixPosition() {
218-
const $container = $(this.suggestionsContainer);
222+
const $container = this.$container;
219223
const containerParent = $container.parent().get(0);
220224
if (containerParent !== document.body && !this.options.forceFixPosition) {
221225
return;
@@ -442,16 +446,15 @@ var _Autocomplete = class _Autocomplete {
442446
return this.badQueries.some((bad) => q.indexOf(bad) === 0);
443447
}
444448
hide() {
445-
const container = $(this.suggestionsContainer);
446449
if (this.options.onHide && this.visible) {
447-
this.options.onHide.call(this.element, container);
450+
this.options.onHide.call(this.element, this.$container);
448451
}
449452
this.visible = false;
450453
this.selectedIndex = -1;
451454
if (this.onChangeTimeout) {
452455
clearTimeout(this.onChangeTimeout);
453456
}
454-
container.hide();
457+
this.$container.hide();
455458
this.onHint(null);
456459
}
457460
suggest() {
@@ -468,8 +471,7 @@ var _Autocomplete = class _Autocomplete {
468471
const value = this.getQuery(this.currentValue);
469472
const className = this.classes.suggestion;
470473
const classSelected = this.classes.selected;
471-
const container = $(this.suggestionsContainer);
472-
const noSuggestionsContainer = $(this.noSuggestionsContainer);
474+
const container = this.$container;
473475
if (options.triggerSelectOnValidInput && this.isExactMatch(value)) {
474476
this.select(0);
475477
return;
@@ -488,7 +490,7 @@ var _Autocomplete = class _Autocomplete {
488490
return `${group}<div class="${className}" data-index="${i}">${formatResultFn(suggestion, value, i)}</div>`;
489491
}).join("");
490492
this.adjustContainerWidth();
491-
noSuggestionsContainer.detach();
493+
this.$noSuggestionsContainer.detach();
492494
container.html(html);
493495
beforeRender?.call(this.element, container, this.suggestions);
494496
this.fixPosition();
@@ -503,25 +505,22 @@ var _Autocomplete = class _Autocomplete {
503505
}
504506
noSuggestions() {
505507
const { beforeRender } = this.options;
506-
const container = $(this.suggestionsContainer);
507-
const noSuggestionsContainer = $(this.noSuggestionsContainer);
508+
const container = this.$container;
508509
this.adjustContainerWidth();
509-
noSuggestionsContainer.detach();
510-
container.empty();
511-
container.append(noSuggestionsContainer);
510+
this.$noSuggestionsContainer.detach();
511+
container.empty().append(this.$noSuggestionsContainer);
512512
beforeRender?.call(this.element, container, this.suggestions);
513513
this.fixPosition();
514514
container.show();
515515
this.visible = true;
516516
}
517517
adjustContainerWidth() {
518518
const { width } = this.options;
519-
const container = $(this.suggestionsContainer);
520519
if (width === "auto") {
521520
const w = this.el.outerWidth() ?? 0;
522-
container.css("width", w > 0 ? w : 300);
521+
this.$container.css("width", w > 0 ? w : 300);
523522
} else if (width === "flex") {
524-
container.css("width", "");
523+
this.$container.css("width", "");
525524
}
526525
}
527526
findBestHint() {
@@ -571,7 +570,7 @@ var _Autocomplete = class _Autocomplete {
571570
}
572571
activate(index) {
573572
const selected = this.classes.selected;
574-
const container = $(this.suggestionsContainer);
573+
const container = this.$container;
575574
const children = container.find(`.${this.classes.suggestion}`);
576575
container.find(`.${selected}`).removeClass(selected);
577576
this.selectedIndex = index;
@@ -594,7 +593,7 @@ var _Autocomplete = class _Autocomplete {
594593
return;
595594
}
596595
if (this.selectedIndex === 0) {
597-
$(this.suggestionsContainer).children(`.${this.classes.suggestion}`).first().removeClass(this.classes.selected);
596+
this.$container.children(`.${this.classes.suggestion}`).first().removeClass(this.classes.selected);
598597
this.selectedIndex = -1;
599598
this.ignoreValueChange = false;
600599
this.el.val(this.currentValue);
@@ -616,7 +615,7 @@ var _Autocomplete = class _Autocomplete {
616615
}
617616
const heightDelta = $(activeItem).outerHeight() ?? 0;
618617
const offsetTop = activeItem.offsetTop;
619-
const container = $(this.suggestionsContainer);
618+
const container = this.$container;
620619
const upperBound = container.scrollTop() ?? 0;
621620
const lowerBound = upperBound + this.options.maxHeight - heightDelta;
622621
if (offsetTop < upperBound) {
@@ -659,7 +658,7 @@ var _Autocomplete = class _Autocomplete {
659658
if (this.fixPositionCapture) {
660659
$(window).off("resize.autocomplete", this.fixPositionCapture);
661660
}
662-
$(this.suggestionsContainer).remove();
661+
this.$container.remove();
663662
}
664663
};
665664
_Autocomplete.defaults = defaults;

dist/jquery.autocomplete.js

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,17 @@
129129
const selected = this.classes.selected;
130130
const options = this.options;
131131
this.element.setAttribute("autocomplete", "off");
132-
this.noSuggestionsContainer = $2('<div class="autocomplete-no-suggestion"></div>').html(options.noSuggestionNotice).get(0);
132+
this.$noSuggestionsContainer = $2('<div class="autocomplete-no-suggestion"></div>').html(
133+
options.noSuggestionNotice
134+
);
135+
this.noSuggestionsContainer = this.$noSuggestionsContainer.get(0);
133136
this.suggestionsContainer = _Autocomplete.utils.createNode(options.containerClass);
134-
const container = $2(this.suggestionsContainer);
135-
container.appendTo(options.appendTo || "body");
137+
this.$container = $2(this.suggestionsContainer);
138+
this.$container.appendTo(options.appendTo || "body");
136139
if (options.width !== "auto") {
137-
container.css("width", options.width);
140+
this.$container.css("width", options.width);
138141
}
142+
const container = this.$container;
139143
container.on("mouseover.autocomplete", suggestionSelector, function() {
140144
self.activate($2(this).data("index"));
141145
});
@@ -196,7 +200,7 @@
196200
options.lookup = this.verifySuggestionsFormat(options.lookup);
197201
}
198202
options.orientation = this.validateOrientation(options.orientation, "bottom");
199-
$2(this.suggestionsContainer).css({
203+
this.$container.css({
200204
"max-height": `${options.maxHeight}px`,
201205
width: `${options.width}px`,
202206
"z-index": options.zIndex
@@ -223,7 +227,7 @@
223227
this.disabled = false;
224228
}
225229
fixPosition() {
226-
const $container = $2(this.suggestionsContainer);
230+
const $container = this.$container;
227231
const containerParent = $container.parent().get(0);
228232
if (containerParent !== document.body && !this.options.forceFixPosition) {
229233
return;
@@ -450,16 +454,15 @@
450454
return this.badQueries.some((bad) => q.indexOf(bad) === 0);
451455
}
452456
hide() {
453-
const container = $2(this.suggestionsContainer);
454457
if (this.options.onHide && this.visible) {
455-
this.options.onHide.call(this.element, container);
458+
this.options.onHide.call(this.element, this.$container);
456459
}
457460
this.visible = false;
458461
this.selectedIndex = -1;
459462
if (this.onChangeTimeout) {
460463
clearTimeout(this.onChangeTimeout);
461464
}
462-
container.hide();
465+
this.$container.hide();
463466
this.onHint(null);
464467
}
465468
suggest() {
@@ -476,8 +479,7 @@
476479
const value = this.getQuery(this.currentValue);
477480
const className = this.classes.suggestion;
478481
const classSelected = this.classes.selected;
479-
const container = $2(this.suggestionsContainer);
480-
const noSuggestionsContainer = $2(this.noSuggestionsContainer);
482+
const container = this.$container;
481483
if (options.triggerSelectOnValidInput && this.isExactMatch(value)) {
482484
this.select(0);
483485
return;
@@ -496,7 +498,7 @@
496498
return `${group}<div class="${className}" data-index="${i}">${formatResultFn(suggestion, value, i)}</div>`;
497499
}).join("");
498500
this.adjustContainerWidth();
499-
noSuggestionsContainer.detach();
501+
this.$noSuggestionsContainer.detach();
500502
container.html(html);
501503
beforeRender?.call(this.element, container, this.suggestions);
502504
this.fixPosition();
@@ -511,25 +513,22 @@
511513
}
512514
noSuggestions() {
513515
const { beforeRender } = this.options;
514-
const container = $2(this.suggestionsContainer);
515-
const noSuggestionsContainer = $2(this.noSuggestionsContainer);
516+
const container = this.$container;
516517
this.adjustContainerWidth();
517-
noSuggestionsContainer.detach();
518-
container.empty();
519-
container.append(noSuggestionsContainer);
518+
this.$noSuggestionsContainer.detach();
519+
container.empty().append(this.$noSuggestionsContainer);
520520
beforeRender?.call(this.element, container, this.suggestions);
521521
this.fixPosition();
522522
container.show();
523523
this.visible = true;
524524
}
525525
adjustContainerWidth() {
526526
const { width } = this.options;
527-
const container = $2(this.suggestionsContainer);
528527
if (width === "auto") {
529528
const w = this.el.outerWidth() ?? 0;
530-
container.css("width", w > 0 ? w : 300);
529+
this.$container.css("width", w > 0 ? w : 300);
531530
} else if (width === "flex") {
532-
container.css("width", "");
531+
this.$container.css("width", "");
533532
}
534533
}
535534
findBestHint() {
@@ -579,7 +578,7 @@
579578
}
580579
activate(index) {
581580
const selected = this.classes.selected;
582-
const container = $2(this.suggestionsContainer);
581+
const container = this.$container;
583582
const children = container.find(`.${this.classes.suggestion}`);
584583
container.find(`.${selected}`).removeClass(selected);
585584
this.selectedIndex = index;
@@ -602,7 +601,7 @@
602601
return;
603602
}
604603
if (this.selectedIndex === 0) {
605-
$2(this.suggestionsContainer).children(`.${this.classes.suggestion}`).first().removeClass(this.classes.selected);
604+
this.$container.children(`.${this.classes.suggestion}`).first().removeClass(this.classes.selected);
606605
this.selectedIndex = -1;
607606
this.ignoreValueChange = false;
608607
this.el.val(this.currentValue);
@@ -624,7 +623,7 @@
624623
}
625624
const heightDelta = $2(activeItem).outerHeight() ?? 0;
626625
const offsetTop = activeItem.offsetTop;
627-
const container = $2(this.suggestionsContainer);
626+
const container = this.$container;
628627
const upperBound = container.scrollTop() ?? 0;
629628
const lowerBound = upperBound + this.options.maxHeight - heightDelta;
630629
if (offsetTop < upperBound) {
@@ -667,7 +666,7 @@
667666
if (this.fixPositionCapture) {
668667
$2(window).off("resize.autocomplete", this.fixPositionCapture);
669668
}
670-
$2(this.suggestionsContainer).remove();
669+
this.$container.remove();
671670
}
672671
};
673672
_Autocomplete.defaults = defaults;

0 commit comments

Comments
 (0)