Skip to content

Commit 494836a

Browse files
committed
Fix #381 search request being sent when an item is selected
Fix #380 result container stays open on choosing an entry when `dynamic: true` Improve `searchOnFocus` behaviour when an input is pre-filled and the Typeahead input is focused
1 parent 620ed4c commit 494836a

File tree

6 files changed

+94
-55
lines changed

6 files changed

+94
-55
lines changed

dist/jquery.typeahead.min.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/jquery.typeahead.js

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Licensed under the MIT license
55
*
66
* @author Tom Bertrand
7-
* @version 2.10.1 (2017-10-4)
7+
* @version 2.10.1 (2017-10-9)
88
* @link http://www.runningcoder.org/jquerytypeahead/
99
*/
1010
(function (factory) {
@@ -180,6 +180,7 @@
180180
this.generateGroups = []; // Array of groups to generate when Typeahead requests data
181181
this.requestGroups = []; // Array of groups to request via Ajax
182182
this.result = []; // Results based on Source-query match (only contains the displayed elements)
183+
this.tmpResult = {}; // Temporary object of results, before they get passed to the buildLayout function
183184
this.groupTemplate = ""; // Result template at the {{group}} level
184185
this.resultHtml = null; // HTML Results (displayed elements)
185186
this.resultCount = 0; // Total results based on Source-query match
@@ -690,8 +691,9 @@
690691
scope.buildBackdropLayout();
691692
scope.showLayout();
692693
}
693-
if (scope.options.searchOnFocus) {
694+
if (scope.options.searchOnFocus && !scope.item) {
694695
scope.deferred = $.Deferred();
696+
scope.assignQuery();
695697
scope.generateSource();
696698
}
697699
break;
@@ -724,12 +726,7 @@
724726
}
725727
case "input":
726728
scope.deferred = $.Deferred();
727-
if (scope.isContentEditable) {
728-
scope.rawQuery = scope.node.text();
729-
} else {
730-
scope.rawQuery = scope.node.val().toString();
731-
}
732-
scope.query = scope.rawQuery.replace(/^\s+/, "");
729+
scope.assignQuery();
733730

734731
// #195 Trigger an onCancel event if the Typeahead is cleared
735732
if (scope.rawQuery === "" && scope.query === "") {
@@ -791,6 +788,20 @@
791788
}
792789
},
793790

791+
assignQuery: function () {
792+
if (this.isContentEditable) {
793+
this.rawQuery = this.node.text();
794+
} else {
795+
this.rawQuery = this.node.val().toString();
796+
}
797+
this.rawQuery = this.rawQuery.replace(/^\s+/, "");
798+
799+
if (this.rawQuery !== this.query) {
800+
this.item = null;
801+
this.query = this.rawQuery;
802+
}
803+
},
804+
794805
filterGenerateSource: function () {
795806
this.searchGroups = [];
796807
this.generateGroups = [];
@@ -1511,7 +1522,6 @@
15111522
}
15121523

15131524
this.options.loadingAnimation && this.container.removeClass("loading");
1514-
15151525
this.node.trigger("search" + this.namespace);
15161526
},
15171527

@@ -1568,16 +1578,19 @@
15681578
]);
15691579

15701580
if (e.keyCode === 13) {
1581+
// Chrome needs preventDefault else the input search event is triggered
1582+
e.preventDefault();
15711583
if (activeItem.length > 0) {
1572-
// Prevent form submit if an element is selected
1573-
e.preventDefault();
1574-
15751584
// #311 When href is defined and "enter" is pressed, it needs to act as a "clicked" link
15761585
if (activeItem.find("a:first")[0].href === "javascript:;") {
15771586
activeItem.find("a:first").trigger("click", e);
15781587
} else {
15791588
activeItem.find("a:first")[0].click();
15801589
}
1590+
} else {
1591+
this.node
1592+
.closest("form")
1593+
.trigger("submit");
15811594
}
15821595
return;
15831596
}
@@ -1852,15 +1865,15 @@
18521865
? group
18531866
: item[groupBy] ? item[groupBy] : item.group;
18541867

1855-
if (groupReference && !this.result[groupReference]) {
1856-
this.result[groupReference] = [];
1868+
if (groupReference && !this.tmpResult[groupReference]) {
1869+
this.tmpResult[groupReference] = [];
18571870
this.resultCountPerGroup[groupReference] = 0;
18581871
}
18591872

18601873
if (maxItemPerGroup) {
18611874
if (
18621875
groupBy === "group" &&
1863-
this.result[groupReference].length >= maxItemPerGroup && !this.options.callback.onResult
1876+
this.tmpResult[groupReference].length >= maxItemPerGroup && !this.options.callback.onResult
18641877
) {
18651878
break;
18661879
}
@@ -1970,12 +1983,12 @@
19701983
if (this.resultItemCount < maxItem) {
19711984
if (
19721985
maxItemPerGroup &&
1973-
this.result[groupReference].length >= maxItemPerGroup
1986+
this.tmpResult[groupReference].length >= maxItemPerGroup
19741987
) {
19751988
break;
19761989
}
19771990

1978-
this.result[groupReference].push(
1991+
this.tmpResult[groupReference].push(
19791992
$.extend(true, {matchedKey: displayKeys[v]}, item)
19801993
);
19811994
this.resultItemCount++;
@@ -1989,7 +2002,7 @@
19892002
}
19902003
if (
19912004
maxItemPerGroup &&
1992-
this.result[groupReference].length >= maxItemPerGroup
2005+
this.tmpResult[groupReference].length >= maxItemPerGroup
19932006
) {
19942007
if (groupBy === "group") {
19952008
break;
@@ -2018,17 +2031,17 @@
20182031
var displayKeys = [],
20192032
displayKey;
20202033

2021-
for (var group in this.result) {
2022-
if (!this.result.hasOwnProperty(group)) continue;
2023-
for (var i = 0, ii = this.result[group].length; i < ii; i++) {
2034+
for (var group in this.tmpResult) {
2035+
if (!this.tmpResult.hasOwnProperty(group)) continue;
2036+
for (var i = 0, ii = this.tmpResult[group].length; i < ii; i++) {
20242037
displayKey =
2025-
this.options.source[this.result[group][i].group].display ||
2038+
this.options.source[this.tmpResult[group][i].group].display ||
20262039
this.options.display;
20272040
if (!~displayKeys.indexOf(displayKey[0])) {
20282041
displayKeys.push(displayKey[0]);
20292042
}
20302043
}
2031-
this.result[group].sort(
2044+
this.tmpResult[group].sort(
20322045
scope.helper.sort(
20332046
displayKeys,
20342047
scope.options.order === "asc",
@@ -2047,7 +2060,7 @@
20472060
groupOrder = this.options.groupOrder.apply(this, [
20482061
this.node,
20492062
this.query,
2050-
this.result,
2063+
this.tmpResult,
20512064
this.resultCount,
20522065
this.resultCountPerGroup
20532066
]);
@@ -2056,17 +2069,17 @@
20562069
} else if (
20572070
typeof this.options.groupOrder === "string" && ~["asc", "desc"].indexOf(this.options.groupOrder)
20582071
) {
2059-
groupOrder = Object.keys(this.result).sort(
2072+
groupOrder = Object.keys(this.tmpResult).sort(
20602073
scope.helper.sort([], scope.options.groupOrder === "asc", function (a) {
20612074
return a.toString().toUpperCase();
20622075
})
20632076
);
20642077
} else {
2065-
groupOrder = Object.keys(this.result);
2078+
groupOrder = Object.keys(this.tmpResult);
20662079
}
20672080

20682081
for (var i = 0, ii = groupOrder.length; i < ii; i++) {
2069-
concatResults = concatResults.concat(this.result[groupOrder[i]] || []);
2082+
concatResults = concatResults.concat(this.tmpResult[groupOrder[i]] || []);
20702083
}
20712084

20722085
// #286 groupTemplate option was deleting group reference Array
@@ -2428,18 +2441,18 @@
24282441
scope.query = scope.rawQuery = "";
24292442
scope.addMultiselectItemLayout(templateValue);
24302443
} else {
2444+
scope.focusOnly = true;
24312445
scope.query = scope.rawQuery = templateValue;
24322446
if (scope.isContentEditable) {
24332447
scope.node.text(scope.query);
24342448
scope.helper.setCaretAtEnd(scope.node[0]);
24352449
}
24362450
}
24372451

2438-
scope.focusOnly = true;
2452+
scope.hideLayout();
24392453

24402454
scope.node
24412455
.val(scope.query)
2442-
.trigger('input' + scope.namespace)
24432456
.focus();
24442457

24452458
scope.helper.executeCallback.call(
@@ -3041,6 +3054,7 @@
30413054
this.getTemplateValue(data[i])
30423055
);
30433056
}
3057+
30443058
this.node.trigger("search" + this.namespace, { origin: 'populateMultiselectData' });
30453059
},
30463060

@@ -3270,17 +3284,14 @@
32703284
},
32713285

32723286
resetLayout: function () {
3273-
this.result = {};
3287+
this.result = [];
3288+
this.tmpResult = {};
32743289
this.groups = [];
32753290
this.resultCount = 0;
32763291
this.resultCountPerGroup = {};
32773292
this.resultItemCount = 0;
32783293
this.resultHtml = null;
32793294

3280-
if (!this.focusOnly) {
3281-
this.item = null;
3282-
}
3283-
32843295
if (this.options.hint && this.hint.container) {
32853296
this.hint.container.val("");
32863297
if (this.isContentEditable) {

test/integration/callback.submit.test.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('Typeahead onSubmit Callback Tests', () => {
1616
document.body.innerHTML = `<form>
1717
<div class="typeahead__container">
1818
<div class="typeahead__field">
19-
19+
2020
<span class="typeahead__query">
2121
<input class="js-typeahead"
2222
name="q"
@@ -29,7 +29,7 @@ describe('Typeahead onSubmit Callback Tests', () => {
2929
<span class="typeahead__search-icon"></span>
3030
</button>
3131
</span>
32-
32+
3333
</div>
3434
</div>
3535
</form>`;
@@ -59,15 +59,13 @@ describe('Typeahead onSubmit Callback Tests', () => {
5959
});
6060

6161
it('Should call onSubmit callback with with the selected item', () => {
62-
myTypeahead.node.val('test');
63-
myTypeahead.node.trigger('input');
62+
myTypeahead.node.val('test').trigger('input');
6463

6564
myTypeahead.resultContainer.find('li:eq(0) a').trigger('click');
6665

66+
expect(myTypeahead.container.hasClass('result')).toBeFalsy();
6767

6868
myTypeahead.node.trigger(enterEvent);
69-
myTypeahead.node.closest('form').submit();
70-
7169

7270
expect(onSubmitCalled).toBeTruthy();
7371
expect(onSubmitItem).toEqual({
@@ -77,6 +75,8 @@ describe('Typeahead onSubmit Callback Tests', () => {
7775
"matchedKey": "display"
7876
});
7977

78+
expect(myTypeahead.container.hasClass('result')).toBeFalsy();
79+
8080
});
8181
});
8282

@@ -88,15 +88,12 @@ describe('Typeahead onSubmit Callback Tests', () => {
8888
onSubmitCalled,
8989
onSubmitItem;
9090

91-
let enterEvent = $.Event("keydown");
92-
enterEvent.keyCode = 13;
93-
9491
beforeAll(() => {
9592

9693
document.body.innerHTML = `<form>
9794
<div class="typeahead__container">
9895
<div class="typeahead__field">
99-
96+
10097
<span class="typeahead__query">
10198
<input class="js-typeahead"
10299
name="q"
@@ -109,7 +106,7 @@ describe('Typeahead onSubmit Callback Tests', () => {
109106
<span class="typeahead__search-icon"></span>
110107
</button>
111108
</span>
112-
109+
113110
</div>
114111
</div>
115112
</form>`;
@@ -160,5 +157,7 @@ describe('Typeahead onSubmit Callback Tests', () => {
160157
{"display": "callback", "id": 2}
161158
]);
162159

160+
expect(myTypeahead.container.hasClass('result')).toBeFalsy();
161+
163162
});
164163
});

test/integration/event.search.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('Typeahead Init from one Typeahead selector inside configuration', () =
2121

2222
it('Should search / build / hide properly according to configuration', () => {
2323

24-
expect(myTypeahead.result).toEqual({});
24+
expect(myTypeahead.result).toEqual([]);
2525

2626
myTypeahead.node.val('d').trigger('input');
2727
expect(myTypeahead.result.length).toEqual(3);
@@ -33,7 +33,7 @@ describe('Typeahead Init from one Typeahead selector inside configuration', () =
3333
expect(myTypeahead.resultContainer.find('.' + myTypeahead.options.selector.empty).text()).toEqual('No result for "dd"');
3434

3535
myTypeahead.node.val('').trigger('input');
36-
expect(myTypeahead.result).toEqual({});
36+
expect(myTypeahead.result).toEqual([]);
3737
expect(myTypeahead.container.hasClass('result')).toBeFalsy();
3838

3939
});

test/integration/multiselect.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ describe('Typeahead multiselect option Tests', () => {
162162
expect(myTypeahead.label.container.find('.typeahead__label:first > a').attr('href')).toBe('/item/1');
163163

164164
myTypeahead.node.val('group').trigger('input');
165-
expect(myTypeahead.result).toEqual({});
165+
expect(myTypeahead.result).toEqual([]);
166166
expect(myTypeahead.resultContainer.find('.typeahead__empty').text()).toBe('Only 2 item is allowed.');
167167

168168
myTypeahead.label.container.find('.typeahead__label:first > a').trigger('click');

0 commit comments

Comments
 (0)