@alizharb/bootstrap-multiselect
v1.0.3
Published
Bootstrap-styled multiselect dropdown with search, tags, remote data and themes.
Maintainers
Readme
azh-select
Advanced Select Component for Bootstrap 5
Powerful, lightweight, and highly customizable select component with search, tags, remote API loading, custom templates, and full dark mode support.
📖 Documentation • 🚀 Quick Start • 💡 Examples • 🎨 Themes • 🔧 API Reference
📚 Demo & Full Documentation
🔗 Live Demo & Complete Documentation
✨ Features
🎯 Core Features
- Single & Multiple Selection - Standard and multi-select modes
- Real-time Search - Filter options as you type
- Tag Creation - Users can create new options dynamically
- Clear Button - Easy reset functionality
- Keyboard Navigation - Full accessibility support
- Form Integration - Works seamlessly with HTML forms
🚀 Advanced Features
- Remote API Loading - Load data from REST endpoints
- Custom Templates - Rich HTML templates for options
- Dark/Light Themes - Bootstrap 5 theme integration
- Event System - Comprehensive event callbacks
- Responsive Design - Mobile-first responsive layout
- Performance Optimized - Efficient rendering for large datasets
🎯 Quick Start
Installation
Option 1: Direct Download
<!-- CSS -->
<link rel="stylesheet" href="path/to/dist/css/azh-select.min.css" />
<!-- JavaScript -->
<script src="path/to/dist/js/azh-select.min.js"></script>Option 2: CDN
<!-- CSS -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@alizharb/[email protected]/dist/css/azh-select.min.css"
/>
<!-- JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/@alizharb/[email protected]/dist/js/azh-select.min.js"></script>Option 3: NPM
npm install @alizharb/bootstrap-multiselectRequirements
- Bootstrap 5.0+ (Required for styling)
- Modern Browser with ES6+ support
Basic Usage
<!-- Include Bootstrap 5 -->
<link
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<!-- Include azh-select -->
<link rel="stylesheet" href="azh-select.min.css" />
<script src="azh-select.min.js"></script>
<!-- Create your select -->
<select class="form-select azh-select" data-placeholder="Choose an option">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
<!-- Initialize -->
<script>
document.addEventListener("DOMContentLoaded", () => {
azhSelect.init(".azh-select");
});
</script>💡 Examples
Basic Select
<select class="form-select azh-select" data-placeholder="Select an option">
<option value="apple">🍎 Apple</option>
<option value="banana">🍌 Banana</option>
<option value="cherry">🍒 Cherry</option>
</select>Searchable Multi-Select
<select
class="form-select azh-select"
multiple
data-search="true"
data-placeholder="Search and select multiple"
>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="js">JavaScript</option>
<option value="php">PHP</option>
</select>Tags (Creatable Options)
<select
class="form-select azh-select"
multiple
data-search="true"
data-tags="true"
data-placeholder="Type to create new tags"
>
<option value="frontend">Frontend</option>
<option value="backend">Backend</option>
<option value="fullstack">Full Stack</option>
</select>Remote API Integration
<select
class="form-select azh-select"
data-remote="true"
data-endpoint="https://api.example.com/users"
data-search="true"
data-value-field="id"
data-label-field="name"
data-placeholder="Search users..."
></select>Custom Templates
azhSelect.init("#customSelect", {
templateOption: (opt) => {
return `<i class="bi bi-star text-warning me-2"></i>${opt.label}`;
},
templateValue: (opt) => {
return `<span class="badge bg-primary">${opt.label}</span>`;
},
});⚙️ Configuration
Data Attributes
| Attribute | Type | Default | Description |
| ------------------ | ------- | ------------- | --------------------------------- |
| data-search | Boolean | false | Enable search functionality |
| data-clear | Boolean | false | Show clear button |
| data-tags | Boolean | false | Allow tag creation |
| data-remote | Boolean | false | Enable remote data loading |
| data-endpoint | String | null | API endpoint URL |
| data-placeholder | String | "Select..." | Placeholder text |
| data-max | Number | null | Maximum selections (multi-select) |
| data-value-field | String | "id" | API response value field |
| data-label-field | String | "name" | API response label field |
| data-min-query | Number | 1 | Minimum chars for remote search |
| data-delay | Number | 300 | Remote search delay (ms) |
| data-preload | Boolean | false | Preload remote data |
| data-bs-theme | String | "light" | Bootstrap theme |
JavaScript Options
azhSelect.init(".azh-select", {
// Template functions
templateOption: (opt) => opt.label,
templateValue: (opt) => opt.label,
// Internationalization
i18n: {
placeholder: "Select...",
clear: "Clear",
noResults: "No results found",
},
});🎨 Theming
azh-select fully supports Bootstrap 5's theme system with automatic dark/light mode detection.
Theme Integration
<!-- Global theme -->
<html data-bs-theme="dark">
<!-- Individual select theme -->
<select class="form-select azh-select" data-bs-theme="dark">
<option value="1">Dark themed option</option>
</select>
</html>Custom Styling
.my-custom-select {
--azh-bg: #f0f8ff;
--azh-border: #4682b4;
--azh-text: #1e3a8a;
--azh-hover-bg: rgba(70, 130, 180, 0.1);
--azh-selected-bg: rgba(70, 130, 180, 0.2);
}Dynamic Theme Switching
function toggleTheme() {
const html = document.documentElement;
const currentTheme = html.getAttribute("data-bs-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
html.setAttribute("data-bs-theme", newTheme);
}🔧 API Reference
Global Methods
| Method | Parameters | Description |
| ------------------------ | ------------------- | -------------------------- |
| azhSelect.init() | selector, options | Initialize select elements |
| azhSelect.get() | selector | Get instance by selector |
| azhSelect.destroy() | selector | Destroy specific instance |
| azhSelect.destroyAll() | - | Destroy all instances |
Instance Methods
| Method | Description |
| ----------- | --------------------- |
| clear() | Clear all selections |
| open() | Open dropdown |
| close() | Close dropdown |
| toggle() | Toggle dropdown state |
| destroy() | Destroy instance |
Events
| Event | Trigger | Detail |
| ------------------- | -------------------- | ---------------------------- |
| azh:change | Selection changes | {instance} |
| azh:clear | Clear button clicked | {instance} |
| azh:open | Dropdown opens | {instance} |
| azh:close | Dropdown closes | {instance} |
| azh:optionCreated | New tag created | {instance, value} |
| azh:remoteLoaded | Remote data loaded | {instance, query, options} |
| azh:error | Remote API error | {instance, error} |
Event Usage
document
.querySelector("#mySelect")
.addEventListener("azh:change", function (e) {
console.log("Selection changed:", e.target.value);
});
document
.querySelector("#mySelect")
.addEventListener("azh:optionCreated", function (e) {
console.log("New tag created:", e.detail.value);
});🌐 Remote API Integration
Server Requirements
Your API should return JSON arrays:
[
{ "id": 1, "name": "John Doe", "email": "[email protected]" },
{ "id": 2, "name": "Jane Smith", "email": "[email protected]" }
]Search Implementation
When search is enabled, queries are sent as URL parameters:
GET /api/users?q=johnServer Examples
Route::get('/api/users', function (Request $request) {
$query = $request->get('q');
return User::when($query, function ($q) use ($query) {
return $q->where('name', 'LIKE', "%{$query}%")
->orWhere('email', 'LIKE', "%{$query}%");
})
->select('id', 'name', 'email')
->limit(50)
->get();
});app.get("/api/users", (req, res) => {
const query = req.query.q;
let sql = "SELECT id, name, email FROM users";
let params = [];
if (query) {
sql += " WHERE name LIKE ? OR email LIKE ?";
params = [`%${query}%`, `%${query}%`];
}
db.query(sql + " LIMIT 50", params, (err, results) => {
if (err) return res.status(500).json({ error: err.message });
res.json(results);
});
});from django.http import JsonResponse
from django.db.models import Q
def users_api(request):
query = request.GET.get('q', '')
users = User.objects.all()
if query:
users = users.filter(
Q(name__icontains=query) | Q(email__icontains=query)
)
return JsonResponse(
list(users.values('id', 'name', 'email')[:50]),
safe=False
)🔧 Advanced Usage
Form Integration
<form class="needs-validation" novalidate>
<div class="mb-3">
<label class="form-label">Required Field</label>
<select class="form-select azh-select" name="category" required>
<option value="">Select...</option>
<option value="tech">Technology</option>
<option value="design">Design</option>
</select>
<div class="invalid-feedback">Please select a category.</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>Framework Integration
import React, { useEffect, useRef } from "react";
import azhSelect from "azh-select";
function AzhSelectComponent({ options, value, onChange }) {
const selectRef = useRef();
useEffect(() => {
azhSelect.init(selectRef.current);
selectRef.current.addEventListener("azh:change", onChange);
return () => azhSelect.destroy(selectRef.current);
}, []);
return (
<select ref={selectRef} className="form-select azh-select">
{options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
);
}<template>
<select ref="select" class="form-select azh-select" v-model="value">
<option v-for="option in options" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
</template>
<script>
import azhSelect from "azh-select";
export default {
props: ["options", "value"],
mounted() {
azhSelect.init(this.$refs.select);
this.$refs.select.addEventListener("azh:change", () => {
this.$emit("input", this.$refs.select.value);
});
},
beforeDestroy() {
azhSelect.destroy(this.$refs.select);
},
};
</script>🛠️ Development
Building from Source
# Clone the repository
git clone https://github.com/alizharb/azh-select.git
cd azh-select
# Install dependencies
npm install
# Build for production
npm run build
# Development with live reload
npm run dev
# Run tests
npm testProject Structure
azh-select/
├── dist/
│ ├── js/
│ │ ├── azh-select.js
│ │ └── azh-select.min.js
│ ├── css/
│ │ ├── azh-select.css
│ │ └── azh-select.min.css
│ ├── scss/
│ │ └── azh-select.scss
│ └── less/
│ └── azh-select.less
├── types/ # TypeScript typings🧪 Browser Support
| Browser | Version | Notes | | ------- | ------- | --------------- | | Chrome | 60+ | ✅ Full support | | Firefox | 55+ | ✅ Full support | | Safari | 12+ | ✅ Full support | | Edge | 79+ | ✅ Full support | | IE | ❌ | Not supported |
Feature Support
- CSS Custom Properties: All supported browsers
- Fetch API: All supported browsers
- Custom Events: All supported browsers
- ES6 Classes: All supported browsers
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
Ways to Contribute
- 🐛 Bug Reports - Found an issue? Let us know!
- 💡 Feature Requests - Have an idea? Share it!
- 📝 Documentation - Help improve our docs
- 🔧 Code - Submit pull requests
- 🧪 Testing - Help test new features
Development Setup
- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Make your changes and test thoroughly
- Submit a pull request with a clear description
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Bootstrap Team - For the amazing CSS framework
- Contributors - Everyone who has contributed to this project
- Community - For feedback, bug reports, and feature suggestions
📞 Support
- 📖 Documentation: Full Documentation
- 🐛 Bug Reports: GitHub Issues
- 💬 Discussions: GitHub Discussions
- 📧 Email: [email protected]
⭐ Star this repo if you find it useful!
Made with ❤️ by Ali Harb
