Martin AJ
Martin AJ

Reputation: 6697

Cannot use 'in' operator to search for 'sth' in undefined

Here is my code:

.
.
keydown: function(ev) {

    clearTimeout( $(this).data('timer') );
    if ( 'abort' in $(this).data('xhr') ) $(this).data('xhr').abort();       // error here
    var xhr, timer = setTimeout(function() {
        xhr = $.ajax({
            url :  '/files/tags_autocomplete.php',
            dataType : 'JSON',
            success : function (tags) {
            $("ul").html(tags.output);
            }
        });
    }, 500);

    $(this).data({timer : timer, xhr : xhr});
}
.
.

As I've commented, third line throws this error:

Uncaught TypeError: Cannot use 'in' operator to search for 'abort' in undefined

How can I fix it?

Upvotes: 2

Views: 26803

Answers (3)

Ryan
Ryan

Reputation: 14649

The issue here is that the undefined value does not have any properties. You need to perform a check on the return value of data() to check that it isn't undefined.

var xhr = $(this).data('xhr');
if(typeof xhr !== 'undefiend' && xhr.abort) {
    // do your code here
}

Replace your if statement with the above 4 lines of code.

Upvotes: 4

num8er
num8er

Reputation: 19372

Change from:

if ('abort' in $(this).data('xhr') ) $(this).data('xhr').abort(); 

to:

if ($(this).data('xhr') && $(this).data('xhr').abort) {
  $(this).data('xhr').abort(); 
}

Problem was simply checking if object has xhr element. By default it does not exist so it's undefined, and you were asking JS engine to find an element in undefined information that was causing error.

So that's why I added to check if it has .data('xhr') because for JS undefined is treated as false and after that I checked if data('xhr') has abort attribute.

By the way if you want to stop timer when key is pressed it's better to just clear timeout and it will not run AJAX call, so no need to put XHR object to element's data storage:

if($(this).data('timer')) {
  clearTimeout($(this).data('timer'));
}

var timer = setTimeout(function() {
    $.ajax({
        url :  '/files/tags_autocomplete.php',
        dataType : 'JSON',
        success : function (tags) {
          $("ul").html(tags.output);
        }
    });
}, 500);

$(this).data('timer', timer);

or even simpler (without data storage):

if(window.autocompleteTimer) {
  clearTimeout(window.autocompleteTimer);
}

window.autocompleteTimer = setTimeout(function() {
    $.ajax({
        url :  '/files/tags_autocomplete.php',
        dataType : 'JSON',
        success : function (tags) {
          $("ul").html(tags.output);
        }
    });
}, 500);

Upvotes: 2

adeneo
adeneo

Reputation: 318242

The issue is that if the user types again before 500ms has passed, $(this).data('xhr') would be undefined, as it's not yet set to the ajax request.

As we can't use the in operator on undefined, only on objects, the correct solution to both clear the timeout and abort any pending requests, would be to just check if $(this).data('xhr') has been set, and is an object, before checking if it has an abort property

keydown: function(ev) {
  var self = $(this);

  clearTimeout(self.data('timer'));

  if (typeof self.data('xhr') === 'object' && 'abort' in self.data('xhr')) {
    self.data('xhr').abort();
  }

  var timer = setTimeout(function() {
    self.data('xhr', $.ajax({
      url: '/files/tags_autocomplete.php',
      dataType: 'JSON',
      success: function(tags) {
        $("ul").html(tags.output);
      }
    }));
  }, 500);

  self.data('timer', timer);

Upvotes: 2

Related Questions