admin管理员组

文章数量:1180477

I'm loading options into an HTML5 datalist element dynamically. However, the browser attempts to show the datalist before the options have loaded. This results in the list not being shown or sometimes a partial list being shown. Is there any way to refresh the list via JavaScript once the options have loaded?

HTML

<input type="text" id="ingredient" list="ingredients">
<datalist id="ingredients"></datalist>

JavaScript

$("#ingredient").on("keyup", function(event) {
    var value = $(this).val();
    $.ajax({
        url: "/api/ingredients",
        data: {search: value.length > 0 ? value + "*" : ""},
        success: function(ingredients) {
            $("#ingredients").empty();
            for (var i in ingredients) {
                $("<option/>").html(ingredients[i].name).appendTo("#ingredients");
            }
            // Trigger a refresh of the rendered datalist
        }
    });
});

Note: In Chrome and Opera, the entire list is only shown if the user clicks on the input after entering text. However, I'd like the entire list to appear as the user types. Firefox is not a problem, as it appears to refresh the list automatically when the options are updated.

UPDATE

I'm not sure this question has a satisfactory answer, as I believe this is simply a shortcoming of certain browsers. If a datalist is updated, the browser should refresh the list, but some browsers (including Chrome and Opera) simply do not do this. Hacks?

I'm loading options into an HTML5 datalist element dynamically. However, the browser attempts to show the datalist before the options have loaded. This results in the list not being shown or sometimes a partial list being shown. Is there any way to refresh the list via JavaScript once the options have loaded?

HTML

<input type="text" id="ingredient" list="ingredients">
<datalist id="ingredients"></datalist>

JavaScript

$("#ingredient").on("keyup", function(event) {
    var value = $(this).val();
    $.ajax({
        url: "/api/ingredients",
        data: {search: value.length > 0 ? value + "*" : ""},
        success: function(ingredients) {
            $("#ingredients").empty();
            for (var i in ingredients) {
                $("<option/>").html(ingredients[i].name).appendTo("#ingredients");
            }
            // Trigger a refresh of the rendered datalist
        }
    });
});

Note: In Chrome and Opera, the entire list is only shown if the user clicks on the input after entering text. However, I'd like the entire list to appear as the user types. Firefox is not a problem, as it appears to refresh the list automatically when the options are updated.

UPDATE

I'm not sure this question has a satisfactory answer, as I believe this is simply a shortcoming of certain browsers. If a datalist is updated, the browser should refresh the list, but some browsers (including Chrome and Opera) simply do not do this. Hacks?

Share Improve this question edited Oct 28, 2014 at 16:00 David Jones asked Oct 28, 2014 at 14:22 David JonesDavid Jones 10.2k29 gold badges93 silver badges144 bronze badges 7
  • Can't you hide or disable the list at page load, get the options, and than undisable/unhide the list? – Thijs Commented Oct 28, 2014 at 14:27
  • @Thijs: I'm updating the list on keyup events, so it's quite dynamic. I've updated my question to reflect this. – David Jones Commented Oct 28, 2014 at 14:31
  • perhaps always show one item which says "loading..." or some sort of spinning image? – Thijs Commented Oct 28, 2014 at 15:09
  • I found this example raymondcamden.com/2012/6/14/… but I don't know if it can help you. – Ragnar Commented Oct 28, 2014 at 15:26
  • Thanks @Ragnar. His solution suffers the same problem. If you open his demo and then type "mo", then hit backspace, the list fails to update in time (but if you inspect element, the datalist has updated). Works fine in Firefox though. – David Jones Commented Oct 28, 2014 at 15:40
 |  Show 2 more comments

7 Answers 7

Reset to default 9

Quite a long time after question but I found a workaround for IE and Chrome (not tested on Opera and already OK for Firefox).

The solution is to focus the input at the end of success (or done) function like this :

$("#ingredient").on("keyup", function(event) {
var _this = $(this);
var value = _this.val();
$.ajax({
    url: "/api/ingredients",
    data: { search: value.length > 0 ? value + "*" : "" },
    success: function(ingredients) {
        $("#ingredients").empty();
        for (var i in ingredients) {
           $("<option/>").html(ingredients[i].name).appendTo("#ingredients");
        }
        // Trigger a refresh of the rendered datalist
        // Workaround using focus()
        _this.focus();
    }
});

It works on Firefox, Chrome and IE 11+ (perhaps 10).

I had the same problem when updating datalist.

The new values would not show until new input event.

I tried every suggested solutions but nothing using Firefox and updating datalist via AJAX.

However, I solved the problem (for simplicity, I'll use your example):

<input type="text" id="ingredient" list="ingredients" **autocomplete="off"**>
<datalist id="ingredients"></datalist>

$("#ingredient").on("**input**", function(event) { ....}

Autocomplete and input is the couple that solve my problems and it works with Chrome too.

You can probably eliminate the problem if you don't make AJAX request on every key stroke. You can try throttle technique using set/cleatTimeout to issue request after 500ms after the last char typed:

$("#ingredient").on("keyup", function(event) {

    clearTimeout($(this).data('timeout'));

    $(this).data('timeout', setTimeout($.proxy(function() {

        var value = $(this).val();

        $.ajax({
            url: "/api/ingredients",
            data: {search: value.length > 0 ? value + "*" : ""},
            success: function(ingredients) {
                $("#ingredients").empty();
                for (var i = 0; i < ingredients.length; i++) {
                    $("<option/>").html(ingredients[i].name).appendTo("#ingredients");
                }
            }
        });

    }, this), 500));
});

Yoyo gave the correct solution, but here's a better way to structure your inserts into the DOM.

$("#ingredient").on("keyup", function(event) {
var _this = $(this);
var value = _this.val();
$.ajax({
    url: "/api/ingredients",
    data: { search: value.length > 0 ? value + "*" : "" },
    success: function(ingredients) {
        var options = ingredients.map(function(ingredient) {
          var option = document.createElement('option');
          option.value = ingredient.name;
          return option;
        });
        $("#ingredients")
          .empty()
          .append(options);
        // Trigger a refresh of the rendered datalist
        // Workaround using focus()
        _this.focus();
    }
});

Less DOM manipulation

With this refinement, I'm only inserting into the DOM a single time per each successful callback. This cuts down on the browser needing to re-render, and will help improve any "blips" in the view.

Functional Programming and Less Idiomatic jQuery

Here we are using the Array.prototype.map to clean up some of the jQuery and make things a bit less idiomatic. You can see from the ECMA Chart that this function will work in all browsers you are targeting.

Not Hacky

This by no means is hacky. IE appears to be the only browser that doesn't automatically refresh the input to display the new list options. focus() is just a way to ensure the input is refocused which forces a refresh of the view.

This solution works very well in all of the browsers that my company has to support internally, IE10+ Chrome and Firefox.

Place your #ingredients element is inside #container and try this code:

$.ajax({
    url: "/api/ingredients",
    data: {search: value.length > 0 ? value + "*" : ""},
    success: function(ingredients) {
        $("#ingredients").remove();
        var item = $('<datalist id="ingredients"></datalist>');
        for (var i in ingredients) {                
            item.append("<option>"+ ingredients[i].name +"</option>");
        }
        item.appendTo('#container');
    }
});

even better without #container and using jQuery replaceWith():

$.ajax({
    url: "/api/ingredients",
    data: {search: value.length > 0 ? value + "*" : ""},
    success: function(ingredients) {
        var item = $('<datalist id="ingredients"></datalist>');
        for (var i in ingredients) {                
            item.append("<option>"+ ingredients[i].name +"</option>");
        }
        $("#ingredients").replaceWith(item);
    }
});

Your issue is that the AJAX is asynchronous.

You'd actually have to have a callback for the AJAX which you call onSuccess which would then update the datalist. Of course, then you might not have great performance/still have a "skipping" behavior, where your datalist options are lagging behind.

If your list of items from the AJAX isn't too large, you should: 1. load the ENTIRE list into memory array with the first query, then... 1. use a filtering function that is applied to the array each time you have a keyUp event.

I found a solution tested only on GNOME Web (WebKit) that consist on set the 'list' attribute of the input element to empty string and, inmediately after, set it again with the id of the datalist element. Here is the example, supose that your input element is stored in a variable named input_element:

var datalist = document.getElementById(input_element.list.id);
// at this point input_element.list.id is equals to datalist.id
// ... update datalist element here
// And now the trick:
input_element.setAttribute('list','')
input_element.setAttribute('list',datalist.id)

本文标签: jqueryHow do you refresh an HTML5 datalist using JavaScriptStack Overflow