aSelect
The aSelect plugin transforms traditional HTML
<select> elements into enhanced dropdown controls that
display options in a tabular (grid) format.
This provides a more visually rich and powerful selection experience, especially useful when options have multiple data attributes that need to be displayed clearly.
Key Features
- Converts select elements into table-based dropdowns
- Supports both client-side and server-side data
- Pagination for large datasets
- Search and filter functionality
- Full keyboard navigation support
- Maintains original select element behavior
- Customizable events and callbacks
- Responsive design
- Dependent field support
- Clear button option
- Dynamic loading of server-side data
Why aSelect?
Built to solve real-world dropdown challenges that traditional select wrappers struggle with.
Structured, Tabular Data
Most select wrappers support search, but very few support a true tabular layout. Real-world selections often require more context than a single label.
aSelect allows you to display multiple columns—such as Customer Name, Address, and Phone—inside the dropdown itself, without custom popovers or repeated UI logic as your application grows.
Built for Massive Datasets
Dropdowns should not break when your data grows. Whether you have thousands or millions of records, aSelect handles it efficiently.
With server-side integration, pagination, and infinite scrolling, aSelect loads data on demand—keeping forms fast, responsive, and scalable.
Cleaner, Smarter UI
Forms often need additional actions—like adding a new customer when the desired option doesn’t exist.
Instead of cluttering the form with extra buttons near labels, aSelect lets you place contextual actions directly inside the dropdown, near the search—keeping the UI clean and intuitive.
Dependencies
jQuery
Core dependency used for DOM manipulation and events.
Popper.js
Handles dropdown positioning logic.
Bootstrap
Used for styling and tooltip support.
Installation
Include the required dependencies and the aSelect plugin after jQuery. You can use a CDN or host the files locally.
CDN Installation
RecommendedQuickest way to get started. Just copy and paste into your page.
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
crossorigin="anonymous">
<!-- jQuery (Required) -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>
<!-- Popper (Required) -->
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
crossorigin="anonymous"></script>
<!-- Bootstrap JS (Core only — NOT bundle) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.min.js"
integrity="sha384-G/EV+4j2dNv+tEPo3++6LCgdCROaejBqfUeNjuKAiuXbjrxilcCdDz6ZAVfHWe1Y"
crossorigin="anonymous"></script>
<!-- aSelect -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@amrit-maan/aselect/dist/aselect.min.css">
<script src="https://cdn.jsdelivr.net/npm/@amrit-maan/aselect/dist/aselect.umd.min.js"></script>
Local Installation (npm)
Install aSelect locally using npm and host the files yourself. Recommended for ASP.NET Core MVC and bundled environments.
1. Install via npm
npm install @amrit-maan/aselect
2. Copy files to wwwroot
Copy the required files from node_modules into your public assets folder.
node_modules/@amrit-maan/aselect/dist/
node_modules/jquery/dist/
node_modules/bootstrap/dist/
node_modules/@popperjs/core/dist/umd/
3. Reference in your layout
<!-- Dependencies -->
<script src="/lib/jquery/jquery.min.js"></script>
<link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
<script src="/lib/popper/popper.min.js"></script>
<script src="/lib/bootstrap/js/bootstrap.min.js"></script>
<!-- aSelect plugin -->
<link rel="stylesheet" href="/lib/aselect/aselect.min.css">
<script src="/lib/aselect/aselect.umd.min.js"></script>
bootstrap.bundle.min.js.
The bundle already contains Popper. aSelect requires Bootstrap core JS and Popper to be loaded separately.
Ensure jQuery is loaded before aSelect.
Basic Usage
Initialize aSelect on an existing
<select> element to transform it into a
searchable, keyboard-friendly dropdown.
Client-side data
Use this approach when all options are already available in the DOM.
<select id="mySelect">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
$('#mySelect').aSelect();
Live Demo
This select below is transformed using the exact code shown on the left.
<select> remains in the DOM and continues
to fire standard change events.
Table / Grid Layout
If you want a table-like dropdown, you can define additional
columns using HTML data attributes and specify which fields should be displayed
using data-headers.
With Data Attributes
Each option provides multiple attributes that are rendered as columns in the dropdown.
<select id="productSelect" data-headers="intId,nvcName,decPrice,intCategory">
<option value="1"
data-intId="1"
data-nvcName="Product A"
data-decPrice="19.99"
data-intCategory="Electronics">
Product A
</option>
<option value="2"
data-intId="2"
data-nvcName="Product B"
data-decPrice="29.99"
data-intCategory="Clothing">
Product B
</option>
</select>
$('#productSelect').aSelect();
Live Demo
The dropdown below displays each option in a grid format.
data-headers.
The first camelCase segment (e.g. int, nvc,
dec) is treated as a technical prefix and is not displayed.
Header names use typed camelCase notation, where the first segment is a technical identifier (e.g. int, nvc, dec) and is not displayed.
This allows headers to be reused for API field mapping and data type identification.
Multi-select
Use the native multiple attribute on the select.
Selected values display as removable chips. Click chips to deselect.
HTML + JS
Add multiple attribute and initialize normally.
<select id="multiSelect" multiple>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
<script>
$('#multiSelect').aSelect();
</script>
Live Demo
Select multiple values and see them appear as chips.
<select> element.
Server-side Data
For large datasets, load data from the server by providing a URL.
Note: The code below uses a C# ASP.NET Core backend implementation and is just a sample example.
Below is a structured approach showing how I handled server-side dropdown loading. Your implementation may vary.
I initially have:
Initialization Script
<script>
function initializePlugins($context) {
$context.find('select.aSelect').each(function () {
$(this).aSelect(); // if aSelect is already initialized, aSelect ignores this.
});
}
$(document).ready(function () {
initializePlugins($(document));
});
// Run after every jQuery AJAX request completes
$(document).ajaxComplete(function () {
initializePlugins($(document));
});
</script>
Now any code that contains aSelect will be initialized automatically. No need to manually call it every time. Next, we will utilize aSelect data-attributes to set up and fetch dropdown data.
Let’s define a DTO first:
Dropdown DTO
public class DropdownDTO
{
public string chDropdownId { get; set; }
public string nvcSqlQuery { get; set; }
public bool bitImplementPagination { get; set; }
public string vcValColumn { get; set; }
public string vcTextColumn { get; set; }
public string vcSkippedColumns { get; set; }
public string vcSkippedSearchColumns { get; set; }
public Dictionary ColumnMappings { get; set; }
}
Define all dropdowns in a central place. Use a static class DropdownRepository to return DTO based on dropdown id.
Dropdown Repository
using DotNetMvc.DTOs;
namespace DotNetMvc.Repository
{
public class DropdownRepository
{
public List GetAllDropdown()
{
return new List()
{
new DropdownDTO
{
chDropdownId = "Order",
nvcSqlQuery = "SELECT [idOrderId], vcOrderNo , decPrice, vcCustomerName as vcCustomer FROM Order WHERE {{filters}} AND bitActive = 1 ORDER BY vcCompanyName",
bitImplementPagination = true,
vcValColumn = "idOrderId",
vcTextColumn = "vcOrderNo",
vcSkippedColumns = "idOrderId",
ColumnMappings = new Dictionary()
{
["vcCustomer"] = "vcCustomerName"
}
}
};
}
}
}
Add in server provider:
Service Registration
builder.Services.AddSingleton();
Now build an endpoint to serve dropdown data. Here is a controller example:
Common Controller
using DotNetMvc.DTOs;
using DotNetMvc.Repository;
using Microsoft.AspNetCore.Mvc;
namespace DotNetMvc.Controllers;
public class CommonController(DropdownRepository dropDownRepo) : Controller
{
public async Task Dropdown(string id, string search, string start, string length, string valColumn, string selectedValue, bool onlyFetchSelectedRow, bool initialLoad, List headers, Dictionary dependsOn)
{
// Logic to handle dropdown data fetching using dropDownRepo
List listAllDropdowns = dropDownRepo.GetAllDropdown();
DropdownDTO dropdown = listAllDropdowns.First(x => x.chDropdownId == id);
List> data = null; // TODO: Implement your logic. I am leaving this intentionally.. your should know how you should fetch your data.
Dictionary selectedRow = null;
if (onlyFetchSelectedRow)
{
if (string.IsNullOrEmpty(selectedValue))
{
throw new InvalidOperationException("Select value cannot be empty");
}
string sql = dropdown.nvcSqlQuery.Replace("{{filters}}", $"({dropdown.vcValColumn} = @valColumn)"); // this is okay as we in the backend control what is the dropdown.vcValColumn...
var parameters = new Dictionary
{
{ "@valColumn", selectedValue },
};
foreach (var kv in dependsOn)
{
parameters[kv.Key] = kv.Value;
}
selectedRow = null; // TODO: Implement your logic. I am leaving this intentionally.. your should know how you should fetch your data. This should be return if onlyFetchSelectedRow is true. This maybe a Db repo Call
}
else
{
data = null; // TODO: Implement your logic. I am leaving this intentionally.. your should know how you should fetch your data. This maybe a Db repo Call
}
return Json(new { data, selectedRow, dropdown.vcValColumn, dropdown.vcTextColumn, dropdown.bitImplementPagination, dropdown.vcSkippedColumns });
}
}
HTML
Add data-url to the select.
<select
name="orders"
class="form-select aSelect"
data-url="/Common/Dropdown/Orders">
</select>
Initialize
Initialize normally. The plugin will fetch rows from the endpoint.
$('.aSelect').aSelect();
Server response format
{
"bitImplementPagination": true,
"vcSkippedColumns": "id,category",
"vcTextColumn": "name",
"vcValColumn": "id",
"data": [
{"id": 1, "name": "Product A", "price": 19.99, "category": "Electronics"},
{"id": 2, "name": "Product B", "price": 29.99, "category": "Clothing"}
]
}
vcSkippedColumns to hide fields you don’t want displayed as columns.
Configuration Options
Configure aSelect using JavaScript initialization options or HTML data attributes.
Initialization Options
$('#mySelect').aSelect({
// Basic options
id: 'custom-id', // Custom ID for the instance
placeHolder: 'Select...', // Placeholder text
showClearButton: true, // Show clear button
quickLinks: '<button>Add New</button>', // Custom HTML for quick links
// Server-side options
ajaxUrl: '/api/data', // URL for server-side data
headers: ['id', 'name'], // Column headers
// Callbacks
onOpen: function() { console.log('Dropdown opened'); },
onClose: function() { console.log('Dropdown closed'); },
onChange: function(newValue) { console.log('Selected:', newValue); },
// Dependent fields
dependsOn: [
{ selector: '#divisionId', paramName: 'divisionId' }
],
// AJAX payload
getAjaxDefaultPayload: function() {
return {
userId: 123,
token: 'abc123'
};
}
});
getAjaxDefaultPayload
You may need this option when your server-side endpoint requires authentication, authorization, or additional contextual data (such as tenant, user, or security tokens).
Instead of defining the payload on every aSelect instance,
you can configure it globally once after login.
A common example is sending an Anti-Forgery token with every request.
$.fn.aSelect.defaults = {
getAjaxDefaultPayload: function () {
return {
__RequestVerificationToken: getAntiForgeryToken()
};
}
};
A-Select Plugin Data Attributes
Configure the A-Select plugin directly through HTML data attributes for quick setup.
| Data Attribute | Description | Usage Example |
|---|---|---|
data-url |
Server-side
Endpoint URL for fetching data via AJAX (POST request) |
<select data-url="/api/data"></select>
|
data-headers |
Server-side
Comma-separated list of column names to display in table view |
<select data-headers="id,name,email,department"></select>
|
data-show-clear-button |
UI Feature
Show clear selection button ("true" or "false") |
<select data-show-clear-button="true"></select>
|
data-quicklink |
UI Feature
CSS selector for HTML element to insert as quick links |
<select data-quicklink="#quick-links"></select>
|
data-unique-by |
Validation
Comma-separated selectors to exclude already selected values |
<select data-unique-by=".user-select"></select>
|
data-creatable |
UI Feature
Allow creating new entries from search input ("true" or "false") |
<select data-creatable="true"></select>
|
data-depends-on |
Validation
JSON array of dependent fields that must be selected first |
<select data-depends-on='[{"selector":"#country","param":"countryId"}]'></select>
|
data-hide-excluded |
Validation
Hide excluded rows when using unique-by ("true" or "false") |
<select data-hide-excluded="true"></select>
|
multiple |
Core
Standard HTML attribute to enable multi-selection mode |
<select multiple></select>
|
Complete Example
<select
class="form-select"
data-url="/api/users"
data-headers="id,name,email,role"
data-show-clear-button="true"
data-quicklink="#user-quick-links"
data-unique-by=".user-selector"
data-creatable="true"
data-hide-excluded="true"
data-depends-on='[{"selector":"#department","param":"deptId"}]'
multiple
>
<option value="">Select User</option>
</select>
Quick Links HTML
<div id="user-quick-links">
<button type="button"
class="btn btn-sm btn-outline-primary"
data-value="active">
Active Users
</button>
<button type="button"
class="btn btn-sm btn-outline-success"
data-value="admin">
Administrators
</button>
</div>
Usage Notes:
- Attributes prefixed with
data-are HTML5 compliant - Boolean attributes accept
"true"or"false"string values - JSON format is required for
data-depends-onattribute - Multiple attributes can be combined for complex functionality
- Server-side attributes require proper AJAX endpoint implementation
Methods
Interact with aSelect via standard jQuery patterns.
Get / Set Value
// Get current value
const value = $('#mySelect').val();
// Set value
$('#mySelect').val('2');
Destroy / Refresh
// Remove the aSelect instance
$('#mySelect').aSelect('destroy');
// Refresh the instance
$('#mySelect').aSelect('refresh');
Events
aSelect maintains normal select behavior, so you can keep using standard jQuery events. It also exposes plugin-specific events.
Standard jQuery Events
$('#mySelect').on('change', function() {
console.log('Selected value:', $(this).val());
});
Plugin-specific Events
$('#mySelect').on('aSelect:open', function() {
console.log('Dropdown opened');
});
$('#mySelect').on('aSelect:close', function() {
console.log('Dropdown closed');
});
Advanced Usage
Common advanced patterns for dynamic forms.
Dependent Fields
$('#mySelect').aSelect({
dependsOn: [
{ selector: '#country', paramName: 'countryId' },
{ selector: '#region', paramName: 'regionId' }
]
});
Custom AJAX Payload
$('#serverSelect').aSelect({
getAjaxDefaultPayload: function() {
return {
department: 'sales',
region: 'north'
};
}
});
Handling Disabled State
// Disable the select
$('#mySelect').prop('disabled', true);
// Enable the select
$('#mySelect').prop('disabled', false);
Keyboard Navigation
Supported keys:
Styling
The plugin adds CSS classes you can customize:
.a-select-wrapper.a-select-display.a-select-dropdown.a-select-table-container.a-select-table.a-select-filter-container.a-select-clear-button.a-select-open.a-select-disabledBrowser Compatibility
The aSelect plugin supports all modern browsers and Internet Explorer 11+.
- Chrome (latest)
- Firefox (latest)
- Edge
- Safari
- Internet Explorer 11+
When using Internet Explorer 11, you may need to include polyfills for:
MutationObserverPromise