Skip to main content

Search and Top Navigation

#7555 closed bug (fixed)

Opened July 17, 2011 08:00AM UTC

Closed December 03, 2012 08:19PM UTC

Last modified December 13, 2012 01:55PM UTC

autoFocus randomly deletes typed characters

Reported by: feklee Owned by:
Priority: minor Milestone: 1.9.0
Component: ui.autocomplete Version: 1.8.14
Keywords: Cc:
Blocked by: Blocking:
Description

How I experienced the problem:

1. Opened one of: IE8, Chrome 13.0, Opera 11.50 on WinXP/32

2. Went to: http://jsbin.com/imowet

3. Quickly typed into the input field: "java". In the input field, I saw: "j", "ja", "jav", "jaa". Notice that the "v" got deleted.

Notes:

  • Disabling "autoFocus" makes the problem go away.
  • You may want to adjust the delay and timeout. However, I was able to reproduce the problem with pretty much any combination I tried.

See also: http://forum.jquery.com/topic/autocomplete-ie7-backspace-issue#14737000002558270

Affected jQuery UI version: 1.8.14 (with jQuery 1.6.2)

Attachments (0)
Change History (28)

Changed August 02, 2011 09:54PM UTC by scottgonzalez comment:1

#7597 is a duplicate of this ticket.

Changed August 02, 2011 10:01PM UTC by sexconker comment:2

Was searching high and low for this problem last week, and decided to file a bug report today.

Somehow I didn't land upon this page, so I ended up making that dupe.

Good to see it's not just us.

We also noticed the effect happening less frequently with higher delays, but it would still happen.

The easiest way to reproduce it is to type a string in, then backspace through it (individual key presses, not holding backspace).

Changed September 23, 2011 04:04PM UTC by carl@stonefintech.com comment:3

Found this workaround that seems to be working for me - modify the "$( ".ui-autocomplete-input" ).live( "autocompleteopen", function()" function at the very end of jQuery-ui-1.8.11.js as found here:

https://github.com/davemyers/jquery-ui-extensions/blob/master/autocomplete/jquery.ui.autocomplete.selectFirst.js

Changed October 18, 2011 07:07AM UTC by jbergenthal comment:4

I've found menu.blur is the problem. It is meant to put the old value when the user presses ESC.

However, menu.blur gets also called every time new data comes from the server.

So in the sequence: request-new-data, user-types-something, data-response

menu.blur sets the old value and what the user typed is lost.

Changed October 18, 2011 03:09PM UTC by scottgonzalez comment:5

Thanks @jbergenthal. I actually removed this code the other day, so this should be fixed. We've had a lot of trouble reproducing this bug (though we've seen it so we know it's valid). If you've been able to reliably reproduce this, can you test against master and verify that it's fixed now?

Changed October 18, 2011 03:23PM UTC by scottgonzalez comment:6

resolution: → fixed
status: newclosed

I was able to confirm. Thanks again jbergenthal!

Changed October 18, 2011 03:23PM UTC by scottgonzalez comment:7

Fixed in ea19645c20b79aea8afbc95999a51b4923a0b14b.

Changed March 18, 2012 05:36PM UTC by huemorgan comment:8

_comment0: '''The problem is not fixed.''' and I can reproduce it, \ I changed the JSbin example above a bit and was able to reproduce it easily \ http://jsbin.com/akalon/5/edit#preview - instructions inside \ \ \ I'm using jquery-ui 1..8.16, jquery 1.7.1 \ but it's not working in the example with 1.8.14 and jquery 1.6.2 as well \ on chrome 17.0.963.79 - not that it matters ;) \ \ the problem exist when autoFocus=true delay>=50 ms \ if i set delay=0 there is no problem. \ \ the bug is the same - deleting characters randomly when a response comes back.1332092275970069

The problem is not fixed. and I can reproduce it,

I changed the JSbin example above a bit and was able to reproduce it easily

http://jsbin.com/akalon/5/edit#preview - instructions inside

I'm using jquery-ui 1..8.16, jquery 1.7.1

but it's not working in the example with 1.8.14 and jquery 1.6.2 as well

on chrome 17.0.963.79 - not that it matters ;)

the problem exist when autoFocus=true delay>=50 ms

if i set delay=0 there is no problem.

the bug is the same - deleting characters randomly when a response comes back.

Please help - nothing would please me more then fixing this horrible bug

Changed March 18, 2012 06:47PM UTC by scottgonzalez comment:9

@huemorgan. This *is* fixed. Note the milestone of 1.9, the fix does not exist in 1.8.16, so it's obviously still broken there.

Changed March 19, 2012 09:17AM UTC by NielsJanssen comment:10

I'm running into the same issue. Is it possible for this fix to be merged into 1.8.19? As it seems to be a relatively minor fix for a problem which is causing me quite a headache.

Changed March 19, 2012 11:58AM UTC by scottgonzalez comment:11

Feel free to send a pull request.

Changed August 18, 2012 09:01AM UTC by ninjaxify comment:12

Hey Folks,

I am using the latest version of JqueryUI(1.8.23) and this issue is still there.

This is driving me up the wall, given that the basic experience of typing text in my autocomplete active text box is not working!

If this bug is fixed, can I get a patch for this? This bugs is preventing me from using autocomplete on my production system, and a patch would be really helpful!

Changed August 20, 2012 04:34PM UTC by sexconker comment:13

Replying to [comment:12 ninjaxify]:

Hey Folks, I am using the latest version of JqueryUI(1.8.23) and this issue is still there. This is driving me up the wall, given that the basic experience of typing text in my autocomplete active text box is not working! If this bug is fixed, can I get a patch for this? This bugs is preventing me from using autocomplete on my production system, and a patch would be really helpful!

See http://bugs.jqueryui.com/ticket/8234 .

The race condition exists if you're using a custom source and jQueryUI 1.8.18 or earlier , though I haven't tested with 1.8.19 to verify that it's fixed.

We're still using 1.8.18 in some places and we just use the following:

source: function( request, response ) {
	$("#autocompleteInput").data('rIndex', $("#autocompleteInput").data('rIndex') + 1);

	if ( this.xhr )
		this.xhr.abort();

	this.xhr = 								

	$.ajax({
		url: "your_ajax_url",
		data: {
			data1: 1,
			data2: 2
		},

		context: {
			autocompleteRequest: $("#autocompleteInput").data('rIndex')
		},											
		
		success: function( data ) {
			if ( this.autocompleteRequest === $("#autocompleteInput").data('rIndex') ) {
				response( jQuery.map(data.split("|"), function(n, i) {
					return {
						label: n,
						value: n
					};
				}));
			}
		}
	});
},

We just add a request counter (rIndex) whenever source fires, and abort any existing requests. Then we do our new request as usual, and then on success we verify that our request index is equal to the expected value.

Changed November 17, 2012 11:16PM UTC by Syryos comment:14

This is still an annoying bug and needs a fix.

Observed in jquery 1.8.2 and jquery-ui 1.9.1

Please reopen !

Changed November 19, 2012 02:02AM UTC by tj.vantoll comment:15

@Syryos I cannot create this using the example originally provided using 1.9.1 - http://jsbin.com/imowet/9/edit.

If this is still a problem and you would like us to look into this we'll need a reduced test case showing the issue. To get you started, use this boilerplate: http://jsfiddle.net/ZgAqH/ Open the link and click to "Fork" (in the top menu) to get started.

Changed November 22, 2012 10:16AM UTC by Syryos comment:16

_comment0: I made a further analysis, conclusion so far is: \ \ When the suggestions-callback is triggered, but a certain result is not available \ \ for example \ \ - when a single server JSON response(*) is incorrect JSON format, for example missing closing bracket \ - when the server response is too slow (while the user still types in the #input field \ \ the Return key submits or tries to submit the broken or incorrect "focus" result. \ \ (*) with this is mean this reproducible test scenario when the server response ist correct for "j", "ja", "java", "javas" but is broken for "jav" [sic] \ \ - j -> ok \ - ja -> ok \ - jav -> broken (*) \ - java -> ok \ - javas -> ok \ \ user quickly clicks enter (user wants to submit "java" - but the response to (*) is coming later, overwrites the response to "javas" and in consequence, enter submits (*) instead of "javas" \ \ \ As an ad-hoc fix I suggest - this must be done inside the autocomplete code - to always: \ \ submit ONLY if the current input matches the response (which of course must mirror the original request string) \ \ $("#input").val() === item.requestValue \ \ _and_ to clear all pending ajax requests before submitting again. \ (Perhaps, you do not understand what I mean, then I will reformulate.) \ \ When I have time, I will look into the code and propose a patch. \ 1353579468705356

I made a further analysis, conclusion so far is:

When the suggestions-callback is triggered, but a certain result is not available

for example

  • when a single server JSON response(*) is incorrect JSON format, for example missing closing bracket
  • when the server response is too slow (while the user still types in the #input field

the Return key submits or tries to submit the broken or incorrect "focus" result.

(*) for example, when the server response ist correct for "j", "ja", "java", "javas" but is broken for "jav" [sic]

  • j -> ok
  • ja -> ok
  • jav -> broken (*)
  • java -> ok
  • javas -> ok

user quickly clicks enter (user wants to submit "java" - but the response to (*) is coming later, overwrites the response to "javas" and in consequence, enter submits (*) instead of "javas"

As an ad-hoc fix I suggest - this must be done inside the autocomplete code - to always:

submit ONLY if the current input matches the response (which of course must mirror the original request string)

$("#input").val() === item.requestValue

_and_ to clear all pending ajax requests before submitting again.

(Perhaps, you do not understand what I mean, then I will reformulate.)

When I have time, I will look into the code and propose a patch.

Changed November 22, 2012 11:54AM UTC by mikesherov comment:17

milestone: 1.9.0
resolution: fixed
status: closedreopened

Syryos, thanks for the persistence! I'll reopen this considering you're going to propose a patch.

Changed November 22, 2012 11:55AM UTC by mikesherov comment:18

milestone: → 1.10.0
status: reopenedopen

Changed November 29, 2012 06:05PM UTC by scottgonzalez comment:19

Syryos, What do you mean by "submit"? I thought you meant form submission, but then you say that autocomplete itself should "submit only if ...".

Changed December 03, 2012 08:19PM UTC by scottgonzalez comment:20

milestone: 1.10.01.9.0
resolution: → fixed
status: openclosed

I'm reclosing this. Outstanding requests are aborted as soon as a new search is started and invalid responses are ignored, so I don't see how this could be happening. If you can provide a reduced test case showing the problem, feel free to let us know.

Changed December 04, 2012 10:27PM UTC by Syryos comment:21

tl;dr:

reason found: you can call it bug by design, or unwanted side effect

reprodicible

proposed fix: available. described literally; no code published yet, sorry (but follows)

Pls. reopen issue

@scott.gonzales wrote

I'm reclosing this. Outstanding requests are aborted as soon as a new search is started and invalid responses are ignored (*), so I don't see how this could be happening. If you can provide a reduced test case showing the problem, feel free to let us know.

(*) my analyis based on example code like on the jqui API pages showed, that you are wrong. Each callback in its own context fires a new request, and an older request may overtake newer ones. This is the problem: a single older request which comes later overwrites in consequence the input. This happens for example while very quickly typing characters with autocomplete parameteres min=2 and delay=200.

Can I ask you to please can you leave this issue open ? Why...? Let me explain.

I know, you want to see a patch. I will deliver such a patch, and a working example but haven't the time at the moment. But the good news is, that I now could **finally** solve the issue by applying two fixes !

In my environment, I was able to fully reproduce the effect of "autoFocus randomly deletes typed characters". I found the reason and a solution in the client code.

Allow me first to explain the patch in words (bear with me; a description in words may be difficult to understand). Later (may be mid December 2012), I will publish the code, no cookie-licking, promised.

Analysis:

I found, that indeed my analysis was correct. that it is a race condition:

when the server's response for a previous partial search (example: "jav" <-> old response) is coming after the response of current input "java" - then the old response - the first ajax is not killed - overwrites the new one.

This ***looks*** like "autoFocus randomly deletes characters***, but it is not. Its only reason is that an older server response "overtakes" the current (last) query, and this older server response updated the input div. As you usually type an increasing number of characters, this means, that an "older" server response corresponds to **less** characters then you have typed --> resulting in the effect and issue.

ad-hoc fix:

I could fix this behaviour by expressly deleting _any_ pending ajax request before submitting the new subquery.

A second fix was required for the autofocus=true case: I make now sure that the current $("#input").val() is now always immediately updated from the corresponding server response - when it returns, This overwrites not the elements in the ui-list but only as said the exact response to the user's input.

Proposed:

I will try to apply a correct fix in the autocomplete code itself, if this is possible.

Changed December 04, 2012 10:32PM UTC by scottgonzalez comment:22

We definitely need proof of this, not just you saying that it's a bug. Here's the code that disproves what you're saying: https://github.com/jquery/jquery-ui/blob/3c2acc322782cc08e575405f8123029342e33542/ui/jquery.ui.autocomplete.js#L417 Old requests cannot overwrite new requests as there's a unique id attached to every request and only the most recent request is processed.

Please show code that proves what you're saying. If you can do that, we'll open this bug or file a new one. Until there's proof, this will stay closed.

Changed December 04, 2012 10:42PM UTC by Syryos comment:23

_comment0: The core is \ \ {{{ \ var ajax; \ ... \ \ //attach autocomplete \ $("#input").autocomplete({ \ \ //define callback to format results \ source: function(req, callback){ \ \ //pass request to server \ if (typeof ajax != 'undefined') ajax.abort(); // stop any previous i.e. may be pending ajax requests \ ajax = $.getJSON("http://server/api.php?callback=?", req, function( responseData ) { \ \ .... \ \ }), \ \ ... \ \ select: function( event, ui ) { \ \ // query the server again for the current selection \ \ // overwrite #input with the (echoed) response from the server \ \ // the response comprises (echoes from the server) the input value on which the select event was fired \ \ // in that way I can make 100% sure that the input string is now \ // exactly the same as it was when the user selected an item \ \ // code follows \ }); \ \ }}} \ 1354661008342718
_comment1: The core of my reliable solution (ad-hoc fix) is \ \ {{{ \ var ajax; \ ... \ \ //attach autocomplete \ $("#input").autocomplete({ \ \ //define callback to format results \ source: function(req, callback){ \ \ //pass request to server \ if (typeof ajax != 'undefined') ajax.abort(); // stop any previous i.e. may be pending ajax requests \ ajax = $.getJSON("http://server/api.php?callback=?", req, function( responseData ) { \ \ .... \ \ }), \ \ ... \ \ select: function( event, ui ) { \ \ // query the server again for the current selection \ \ // overwrite #input with the (echoed) response from the server \ \ // the response comprises (echoes from the server) the input value on which the select event was fired \ \ // in that way I can make 100% sure that the input string is now \ // exactly the same as it was when the user selected an item \ \ // code follows \ }); \ \ }}} \ 1354661088764998
_comment2: The core of my reliable solution (ad-hoc fix) is \ \ {{{ \ var ajax; \ ... \ \ //attach autocomplete \ $("#input").autocomplete({ \ \ //define callback to format results \ source: function(req, callback){ \ \ //pass request to server \ if (typeof ajax != 'undefined') ajax.abort(); // stop any previous i.e. may be pending ajax requests \ ajax = $.getJSON("http://server/api.php?callback=?", req, function( responseData ) { \ \ .... \ \ }), \ \ ... \ \ select: function( event, ui ) { \ \ // query the server again for the current selection \ \ // overwrite #input with the (echoed as additional element of the) response from the server \ \ // the response comprises (echoes from the server) the input value on which the select event was fired \ \ // in that way I can make 100% sure that the input string is now \ // exactly the same as it was when the user selected an item \ \ // code follows \ }); \ \ }}} \ 1354661144520516
_comment3: The core of my reliable solution (ad-hoc fix) is \ \ {{{ \ var ajax; \ ... \ \ //attach autocomplete \ $("#input").autocomplete({ \ \ //define callback to format results \ source: function(req, callback){ \ \ //pass request to server \ if (typeof ajax != 'undefined') ajax.abort(); // stop any previous i.e. may be pending ajax requests \ ajax = $.getJSON("http://server/api.php?callback=?", req, function( responseData ) { \ \ .... \ \ }), \ \ ... \ \ select: function( event, ui ) { \ \ // query the server again for the current selection \ \ // overwrite #input with the (echoed) response from the server \ \ // the response comprises (echoed as additional element in the server json respone) the input value on which the select event was fired \ \ // in that way I can make 100% sure that the input string is now \ // exactly the same as it was when the user selected an item \ \ // code follows \ }); \ \ }}} \ 1354661159396697
_comment4: The core of my reliable solution (ad-hoc fix) is \ \ {{{ \ var ajax; \ ... \ \ //attach autocomplete \ $("#input").autocomplete({ \ \ //define callback to format results \ source: function(req, callback){ \ \ //pass request to server \ if (typeof ajax != 'undefined') ajax.abort(); // stop any previous i.e. may be pending ajax requests \ ajax = $.getJSON("http://server/api.php?callback=?", req, function( responseData ) { \ \ .... \ \ }), \ \ ... \ \ select: function( event, ui ) { \ \ // query the server again for the current selection \ \ // overwrite #input with the (echoed) response from the server \ \ // the response comprises (echoed as additional element in the server json response) the input value on which the select event was fired \ \ // in that way I can make 100% sure that the input string is now \ // exactly the same as it was when the user selected an item \ \ // code follows \ }); \ \ }}} \ 1354661306308365

The core of my reliable solution (ad-hoc fix) is

var ajax;
...

//attach autocomplete
$("#input").autocomplete({
					
  //define callback to format results
  source: function(req, callback){

    //pass request to server
    if (typeof ajax != 'undefined') ajax.abort(); // stop any previous i.e. may be pending ajax requests
    ajax = $.getJSON("http://server/api.php?callback=?", req, function( responseData ) {

      ....

    }),

  ...

  select: function( event, ui ) {

    // query the server again for the current selection

    // overwrite #input with the (echoed) response from the server

    // the response comprises (echoed as additional element in the server json response) the input value
    // on which the select event was fired

    // in that way I can make 100% sure that the input string is now
    // exactly the same as it was when the user selected an item

    // code follows
  });

Changed December 04, 2012 10:47PM UTC by Syryos comment:24

_comment0: jquery team: you should perhaps try with lame server responses, let's say responses which need 1 second. This will make the effect apparent.1354661401758981
_comment1: jquery team: you should perhaps try with lame and differently quick server responses, let's say responses which need 1 second and the next response takes 500ms, the next 1s. This will make the effect apparent.1354661428997558

jquery team: you should perhaps try with lame (or differently quick) server responses, let's say responses which need 1 second and the next response takes 500ms, the next 1s. This will make the effect apparent.

Changed December 05, 2012 09:03AM UTC by Syryos comment:25

It has also to do with the "select" callback and a wrong (i.e. presently not corresponding to #input) ui.item.value under the following conditions:

  • fast typing of characters
  • autoFocus == true
  • slow server reponse

Changed December 05, 2012 01:27PM UTC by scottgonzalez comment:26

@Syryos This appears to be specific to your use case, which is highly uncommon. Now that you've shows part of your code it's clear that: 1) You're not using the built-in ajax requests; 2) You're triggering a search in response to a search. Please stop posting on this ticket and file a new ticket. However, I urge you not to open the new ticket until you can provide a reduced test case that shows the problem exists.

Changed December 13, 2012 10:41AM UTC by philios33 comment:27

As jbergenthal says, the problem is caused by the autoFocus option. It will call a hover event on the first menu item after rendering a new menu. Unfortunately upon activating the item, the menu calls deactivate function (first line in activate function) which indeed resets the value in the input box since it triggers a blur.

It only happens if a character is typed AFTER rendering of the data has started but BEFORE the blur function is called. Since the logic in the blur functionality is triggered to be executed in another thread it is possible that by the time it runs, the user will have typed a key. This means that ui.menu thinks the data has changed and will revert it back to what it thinks is the correct data.

This hack will turn off the ui.menu reverting functionality but doesn't fix the underlying problem. You almost need to tell the menu that although you want to highlight the first item, please dont blur the menu when you activate it. I will leave it up to you guys to fix but I suggest removing the deactivate call from the activate function of ui.menu.

//This is a fix for the autoFocus bug
$.widget("ui.menu", $.ui.menu, {
	deactivate: function() {
		if (!this.active) { return; }
		this.active.children("a")
		.removeClass("ui-state-hover")
		.removeAttr("id");
		//DONT DO A BLUR
		//this._trigger("blur");
		this.active = null;
	}
});

@scott.gonzalez The use case described by Syryos is actually quite common. The autocomplete code allows you to override the source function anyway, so I dont see a problem of doing e.g. an async ajax call such as google geocode inside the source function.

Changed December 13, 2012 01:55PM UTC by scottgonzalez comment:28

@philios33 What do you mean by "It will call a hover event on the first menu item" There's no such thing as a hover event, and while 1.8.x used to trigger a synthetic mouseover event, we stopped doing that in 1.9.0.

As for as how common Syryos use case is: I've never seen this behavior, can you point me to some examples? My comment about the custom source is that he wasn't providing any useful information. He has a very complex implementation and wasn't providing any details about what he was actually doing and he couldn't provide a reduced test case showing the problem.

Can you provide a reduced test case along with steps for reproducing this? If you need to go in and modify some code so that whatever delay exists is actually very large to make it easily reproducible that's fine. Or at least point us to a specific line of code if you've tracked this down. But from your description, I still don't know what is causing what you're seeing. Autocomplete doesn't even listen to menu blur events, so I don't see how that's relevant.