Select2 is a jQuery based replacement for select boxes with searching, remote data sets, and infinite scrolling support
—
Remote data loading with customizable request handling, response processing, infinite scrolling, and caching support.
Complete configuration interface for remote data loading with extensive customization options.
/**
* AJAX configuration options
*/
interface AjaxOptions {
url: string | ((params: AjaxParams) => string);
data?: (params: AjaxParams) => object;
dataType?: string;
delay?: number;
cache?: boolean;
transport?: (params: TransportParams, success: Function, failure: Function) => void;
processResults?: (data: any, params: AjaxParams) => AjaxResponse;
// All jQuery.ajax options are also supported
method?: string;
headers?: object;
contentType?: string;
timeout?: number;
}
/**
* AJAX request parameters
*/
interface AjaxParams {
term: string; // Search term entered by user
page: number; // Current page number (starts at 1)
_type?: string; // Request type: 'query' for standard requests
}
/**
* Expected AJAX response format
*/
interface AjaxResponse {
results: DataObject[]; // Array of data objects
pagination?: { // Optional pagination info
more: boolean; // True if more results available
};
}
/**
* Transport function parameters
*/
interface TransportParams {
url: string;
data: object;
success: (data: any) => void;
error: (jqXHR: any, textStatus: string, errorThrown: string) => void;
}Simple AJAX configuration for common remote data scenarios.
/**
* Basic AJAX configuration
*/
ajax: {
url: string | function, // Request URL or function returning URL
dataType: 'json', // Expected response type (default: 'json')
delay: 250, // Debounce delay in milliseconds (default: 250)
cache: false // Enable request caching (default: false)
}Usage Examples:
// Simple AJAX setup
$('#ajax-select').select2({
ajax: {
url: '/api/users',
dataType: 'json'
}
});
// Dynamic URL based on context
$('#context-select').select2({
ajax: {
url: function(params) {
var contextId = $('#context').val();
return '/api/context/' + contextId + '/items';
},
dataType: 'json',
delay: 500
}
});
// With custom headers and authentication
$('#auth-select').select2({
ajax: {
url: '/api/secure-data',
headers: {
'Authorization': 'Bearer ' + authToken,
'X-Requested-With': 'XMLHttpRequest'
},
method: 'POST'
}
});Customize the data sent with AJAX requests to match your API expectations.
/**
* Data transformation function
*/
data?: (params: AjaxParams) => object;Usage Examples:
// Basic parameter mapping
$('#api-select').select2({
ajax: {
url: '/api/search',
data: function(params) {
return {
q: params.term, // Search term
page: params.page, // Page number
limit: 10 // Results per page
};
}
}
});
// Complex parameter transformation
$('#complex-select').select2({
ajax: {
url: '/api/complex-search',
data: function(params) {
var filters = getActiveFilters();
var sortOrder = getSortOrder();
return {
query: params.term || '',
pagination: {
page: params.page || 1,
size: 25
},
filters: filters,
sort: sortOrder,
timestamp: Date.now()
};
}
}
});
// Conditional parameters
$('#conditional-select').select2({
ajax: {
url: '/api/conditional',
data: function(params) {
var requestData = {
search: params.term
};
// Only include page for pagination requests
if (params.page && params.page > 1) {
requestData.page = params.page;
}
// Include category filter if selected
var category = $('#category-filter').val();
if (category) {
requestData.category = category;
}
return requestData;
}
}
});Transform API responses to match Select2's expected data format.
/**
* Response processing function
*/
processResults?: (data: any, params: AjaxParams) => AjaxResponse;Usage Examples:
// Basic response transformation
$('#transform-select').select2({
ajax: {
url: '/api/users',
processResults: function(data, params) {
return {
results: data.users.map(function(user) {
return {
id: user.id,
text: user.name + ' (' + user.email + ')'
};
})
};
}
}
});
// Complex response with pagination
$('#paginated-select').select2({
ajax: {
url: '/api/paginated-data',
data: function(params) {
return {
q: params.term,
page: params.page || 1,
per_page: 20
};
},
processResults: function(data, params) {
params.page = params.page || 1;
return {
results: data.items.map(function(item) {
return {
id: item.uuid,
text: item.display_name,
disabled: !item.active
};
}),
pagination: {
more: (params.page * 20) < data.total_count
}
};
}
}
});
// Grouped results
$('#grouped-ajax').select2({
ajax: {
url: '/api/grouped-data',
processResults: function(data, params) {
return {
results: data.categories.map(function(category) {
return {
text: category.name,
children: category.items.map(function(item) {
return {
id: item.id,
text: item.title
};
})
};
})
};
}
}
});Override the default AJAX transport for complete control over HTTP requests.
/**
* Custom transport function
*/
transport?: (params: TransportParams, success: Function, failure: Function) => void;Usage Examples:
// Fetch API transport
$('#fetch-select').select2({
ajax: {
transport: function(params, success, failure) {
var requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
};
// Add query parameters to URL
var url = new URL(params.url);
Object.keys(params.data).forEach(function(key) {
url.searchParams.append(key, params.data[key]);
});
fetch(url.toString(), requestOptions)
.then(function(response) {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(success)
.catch(failure);
},
processResults: function(data) {
return { results: data };
}
}
});
// Custom HTTP client transport
$('#custom-transport').select2({
ajax: {
transport: function(params, success, failure) {
// Use custom HTTP client (e.g., axios)
customHttpClient.get(params.url, {
params: params.data,
timeout: 5000,
headers: {
'X-API-Key': getApiKey()
}
})
.then(function(response) {
success(response.data);
})
.catch(function(error) {
failure(null, 'error', error.message);
});
}
}
});Enable automatic loading of additional results when scrolling to the bottom.
/**
* Infinite scrolling is automatically enabled when pagination.more is true
*/
// Pagination response format
interface PaginationResponse {
results: DataObject[];
pagination: {
more: boolean; // Indicates more results available
};
}Usage Examples:
// Infinite scroll with page-based pagination
$('#infinite-select').select2({
ajax: {
url: '/api/infinite-data',
data: function(params) {
return {
q: params.term,
page: params.page || 1,
page_size: 50
};
},
processResults: function(data, params) {
params.page = params.page || 1;
return {
results: data.results,
pagination: {
more: data.has_next_page
}
};
}
}
});
// Offset-based infinite scroll
$('#offset-select').select2({
ajax: {
url: '/api/offset-data',
data: function(params) {
var pageSize = 25;
var offset = ((params.page || 1) - 1) * pageSize;
return {
search: params.term,
offset: offset,
limit: pageSize
};
},
processResults: function(data, params) {
params.page = params.page || 1;
var pageSize = 25;
return {
results: data.items,
pagination: {
more: data.total > (params.page * pageSize)
}
};
}
}
});Optimize AJAX requests through caching and request management.
/**
* Caching configuration
*/
cache?: boolean; // Enable built-in caching (default: false)
delay?: number; // Request debounce delay in ms (default: 250)Usage Examples:
// Enable caching for static data
$('#cached-select').select2({
ajax: {
url: '/api/static-reference-data',
cache: true, // Cache results
delay: 100 // Shorter delay for cached data
}
});
// Custom caching implementation
var customCache = {};
$('#custom-cache-select').select2({
ajax: {
transport: function(params, success, failure) {
var cacheKey = params.url + '?q=' + (params.data.q || '');
// Check cache first
if (customCache[cacheKey]) {
setTimeout(function() {
success(customCache[cacheKey]);
}, 50);
return;
}
// Make request and cache result
$.ajax({
url: params.url,
data: params.data,
dataType: 'json'
})
.done(function(data) {
customCache[cacheKey] = data;
success(data);
})
.fail(failure);
}
}
});Handle AJAX errors gracefully with user feedback and retry mechanisms.
// Basic error handling
$('#error-handling-select').select2({
ajax: {
url: '/api/unreliable-endpoint',
transport: function(params, success, failure) {
$.ajax({
url: params.url,
data: params.data,
dataType: 'json',
timeout: 10000
})
.done(success)
.fail(function(jqXHR, textStatus, errorThrown) {
console.error('AJAX Error:', textStatus, errorThrown);
// Provide user feedback
failure(jqXHR, textStatus, 'Failed to load data. Please try again.');
});
},
processResults: function(data, params) {
// Handle malformed responses
if (!data || !Array.isArray(data.results)) {
console.warn('Invalid response format:', data);
return { results: [] };
}
return { results: data.results };
}
}
});
// Retry mechanism
function createRetryTransport(maxRetries = 3) {
return function(params, success, failure) {
var retryCount = 0;
function attemptRequest() {
$.ajax({
url: params.url,
data: params.data,
dataType: 'json',
timeout: 5000
})
.done(success)
.fail(function(jqXHR, textStatus, errorThrown) {
retryCount++;
if (retryCount < maxRetries && textStatus !== 'parsererror') {
console.log('Retrying request, attempt', retryCount + 1);
setTimeout(attemptRequest, 1000 * retryCount);
} else {
failure(jqXHR, textStatus, errorThrown);
}
});
}
attemptRequest();
};
}
$('#retry-select').select2({
ajax: {
transport: createRetryTransport(3)
}
});Install with Tessl CLI
npx tessl i tessl/npm-select2