Twitter Typeahead only seems works with elements called when the page loads (and not the dynamically created stuff made by methods like addWaypoint). How would I resolve this? Please find my code below:

$(document).ready(function () {
    // Add waypoints
    $('#add-waypoint-button').click(function () {
        var n = $('#waypoints input').length;
        addWaypoint(n);
    });

    // Address lookup
    addressLookup();
});

function addWaypoint(waypointCount)
{
    if(waypointCount < 4) {
        $('#waypoints-page #waypoints').append('<div class="waypoint-container"><input type="text" class="form-control booking waypoint" placeholder="Via"><label class="remove-waypoint-button" for=""><i class="fa fa-times"></i></label><hr></div>');
        if ($(window).height() <= 568) {
                $('.form-control').removeClass('input-sm');
                $('.form-control').removeClass('input-lg'); 
                $('.form-control').addClass('input-sm');
            }
            else {
                $('.form-control').removeClass('input-sm');
                $('.form-control').removeClass('input-lg'); 
                $('.form-control').addClass('input-lg');
            }
        }
}

function addressLookup() {
    // Instantiate the Bloodhound suggestion engine
    var addressLookup = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        limit: 5,
        prefetch: {
            url: 'assets/data/countries.json',
            filter: function (list) {
                // Map the remote source JSON array to a JavaScript object array
                return $.map(list, function (addressLookup) {
                    return {
                        name: addressLookup
                    };
                });
            }
        }
    }); 
    // Initialize the Bloodhound suggestion engine
    addressLookup.initialize(); 
    // Instantiate the Typeahead UI
    $('.book-journey-container #pickup, .book-journey-container #destination, .waypoint-container .waypoint').typeahead(null, {
        name: 'addressLookup',
        displayKey: 'name',
        source: addressLookup.ttAdapter()
    });
}

Dani AI

Generated

Short diagnosis: Typeahead binds behavior to DOM nodes at the moment .typeahead() is called, so inputs appended later remain plain inputs. That is the root cause in 's thread — binding happens on page load and never runs again for new waypoint inputs.

Suggested pattern: initialize the Bloodhound engine once, then attach Typeahead to each new input immediately after it is created. Track which inputs have been bound (for example with a data flag) to avoid double-binding. Example pattern:

// create engine once and keep it in scope
var engine = new Bloodhound({ /* config */ });
engine.initialize();

// helper that binds Typeahead to a jQuery input once
function bindTypeahead($input) {
  if ($input.data('taBound')) return;
  $input.typeahead(/* options */, { name: 'addresses', source: engine.ttAdapter() });
  $input.data('taBound', true);
}

// after creating/appending a waypoint input:
var $newInput = $('<input class="waypoint">').appendTo('#waypoints');
bindTypeahead($newInput);

Notes and cautions: 's approach of initializing when loading dynamic content is the same idea — keep engine initialization separate from per-element binding. Prevent double-binding by marking bound inputs instead of re-running global selectors on every addition. When removing inputs, remove the DOM node (or unbind handlers) to avoid leaks, and confirm dataset option names against the installed Typeahead version (some versions use display vs displayKey).

Recommended Answers

All 2 Replies

Just banter m8

Member Avatar for Member #120589

Aha! Yes, faced same issue. This is my lame fix. Don't laugh, I'm a complete DIY disaster area when it comes to js/jq:

<script>
    var bestCustomers = new Bloodhound({
        datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        remote: '/customers/getcustomers/%QUERY',
        limit: 10
    });

    bestCustomers.initialize();

    //then I have a function to get the dynamic input - it may seem a little contrived here as it's part of a far bigger function. I'm cutting it down. i'm also using it as a glorified dropdown, getting the id value, hence the selected stuff aat the bottom.

    function showTypeahead()
    {
        $.get('/forms/frmCustomer')
            .done(function(data){
               $('#remote .typeahead').typeahead({ minLength: 3}, {
                   name: 'best-customers',
                   displayKey: 'value',
                   source: bestCustomers.ttAdapter(),
                   templates: {
                       empty: [
                           '<div class="empty-message">',
                           'Unable to find any customers that match the current query',
                           '</div>'
                       ].join('\n')
                   }
               }).bind("typeahead:selected", function(obj, datum, name) {
                   getCustomerDetails(datum.id);
               });
          });
    }
    </script>    

I look at this now and it gives me a nosebleed. js/jq.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.