<div class="m-multi-select js-m-multi-select" data-multi-select-name="cities">
    <div class="m-multi-select__search">
        <input type="search" class="a-input js-m-multi-select__input" data-multi-select-suggestions="suggestions" aria-expanded="false" aria-controls="multiSelectSuggestionsBox" aria-autocomplete="list" aria-haspopup="listbox" role="combobox" placeholder="Sök städer..." aria-label="Sök städer" aria-describedby="multiSelectDescription" />
        <div class="m-multi-select__suggestions-box js-m-multi-select-suggestions-box" id="multiSelectSuggestionsBox" role="listbox" aria-live="assertive" aria-atomic="true"></div>
    </div>
    <ul class="m-multi-select-selected-items js-m-multi-select-selected-items" aria-live="assertive" aria-atomic="true" aria-multiselectable="true"></ul>
    <div class="u-visuallyhidden" id="multiSelectDescription">Använd upp och ner-pilarna för att lägga till sökträffar och tabb plus enter för att ta bort valda sökträffar</div>
</div>

<script id="suggestions" type="application/json">
    [{
            "name": "New York",
            "value": "new-york"
        },
        {
            "name": "Los Angeles",
            "value": "los-angeles"
        },
        {
            "name": "Chicago",
            "value": "chicago"
        },
        {
            "name": "Houston",
            "value": "houston"
        },
        {
            "name": "Phoenix",
            "value": "phoenix"
        },
        {
            "name": "Philadelphia",
            "value": "philadelphia"
        },
        {
            "name": "San Antonio",
            "value": "san-antonio"
        },
        {
            "name": "San Diego",
            "value": "san-diego"
        },
        {
            "name": "Dallas",
            "value": "dallas"
        },
        {
            "name": "San Jose",
            "value": "san-jose"
        },
        {
            "name": "Austin",
            "value": "austin"
        },
        {
            "name": "Jacksonville",
            "value": "jacksonville"
        },
        {
            "name": "Fort Worth",
            "value": "fort-worth"
        },
        {
            "name": "Columbus",
            "value": "columbus"
        },
        {
            "name": "San Francisco",
            "value": "san-francisco"
        },
        {
            "name": "Tokyo",
            "value": "tokyo"
        },
        {
            "name": "Delhi",
            "value": "delhi"
        },
        {
            "name": "Shanghai",
            "value": "shanghai"
        },
        {
            "name": "Sao Paulo",
            "value": "sao-paulo"
        },
        {
            "name": "Mumbai",
            "value": "mumbai"
        },
        {
            "name": "Beijing",
            "value": "beijing"
        },
        {
            "name": "Cairo",
            "value": "cairo"
        },
        {
            "name": "Dhaka",
            "value": "dhaka"
        },
        {
            "name": "Mexico City",
            "value": "mexico-city"
        },
        {
            "name": "Osaka",
            "value": "osaka"
        },
        {
            "name": "Karachi",
            "value": "karachi"
        },
        {
            "name": "Chongqing",
            "value": "chongqing"
        },
        {
            "name": "Istanbul",
            "value": "istanbul"
        },
        {
            "name": "Buenos Aires",
            "value": "buenos-aires"
        },
        {
            "name": "Kolkata",
            "value": "kolkata"
        },
        {
            "name": "Kinshasa",
            "value": "kinshasa"
        },
        {
            "name": "Lagos",
            "value": "lagos"
        },
        {
            "name": "Manila",
            "value": "manila"
        },
        {
            "name": "Tianjin",
            "value": "tianjin"
        },
        {
            "name": "Rio de Janeiro",
            "value": "rio-de-janeiro"
        },
        {
            "name": "Guangzhou",
            "value": "guangzhou"
        },
        {
            "name": "Lahore",
            "value": "lahore"
        },
        {
            "name": "Moscow",
            "value": "moscow"
        },
        {
            "name": "Shenzhen",
            "value": "shenzhen"
        },
        {
            "name": "Bangalore",
            "value": "bangalore"
        },
        {
            "name": "Paris",
            "value": "paris"
        },
        {
            "name": "Bogota",
            "value": "bogota"
        },
        {
            "name": "Jakarta",
            "value": "jakarta"
        },
        {
            "name": "Chennai",
            "value": "chennai"
        },
        {
            "name": "Lima",
            "value": "lima"
        },
        {
            "name": "Bangkok",
            "value": "bangkok"
        },
        {
            "name": "Seoul",
            "value": "seoul"
        },
        {
            "name": "Nagoya",
            "value": "nagoya"
        },
        {
            "name": "Hyderabad",
            "value": "hyderabad"
        },
        {
            "name": "London",
            "value": "london"
        },
        {
            "name": "Tehran",
            "value": "tehran"
        },
        {
            "name": "Chengdu",
            "value": "chengdu"
        },
        {
            "name": "Nanjing",
            "value": "nanjing"
        },
        {
            "name": "Wuhan",
            "value": "wuhan"
        },
        {
            "name": "Ho Chi Minh City",
            "value": "ho-chi-minh-city"
        },
        {
            "name": "Luanda",
            "value": "luanda"
        },
        {
            "name": "Ahmedabad",
            "value": "ahmedabad"
        },
        {
            "name": "Kuala Lumpur",
            "value": "kuala-lumpur"
        },
        {
            "name": "Xi’an",
            "value": "xi-an"
        },
        {
            "name": "Hong Kong",
            "value": "hong-kong"
        },
        {
            "name": "Dongguan",
            "value": "dongguan"
        },
        {
            "name": "Hangzhou",
            "value": "hangzhou"
        },
        {
            "name": "Foshan",
            "value": "foshan"
        },
        {
            "name": "Shenyang",
            "value": "shenyang"
        },
        {
            "name": "Riyadh",
            "value": "riyadh"
        },
        {
            "name": "Baghdad",
            "value": "baghdad"
        },
        {
            "name": "Santiago",
            "value": "santiago"
        },
        {
            "name": "Surat",
            "value": "surat"
        },
        {
            "name": "Madrid",
            "value": "madrid"
        },
        {
            "name": "Suzhou",
            "value": "suzhou"
        },
        {
            "name": "Pune",
            "value": "pune"
        },
        {
            "name": "Harbin",
            "value": "harbin"
        },
        {
            "name": "Houston",
            "value": "houston"
        },
        {
            "name": "Toronto",
            "value": "toronto"
        },
        {
            "name": "Dar es Salaam",
            "value": "dar-es-salaam"
        },
        {
            "name": "Miami",
            "value": "miami"
        },
        {
            "name": "Belo Horizonte",
            "value": "belo-horizonte"
        },
        {
            "name": "Singapore",
            "value": "singapore"
        },
        {
            "name": "Atlanta",
            "value": "atlanta"
        },
        {
            "name": "Fukuoka",
            "value": "fukuoka"
        },
        {
            "name": "Khartoum",
            "value": "khartoum"
        },
        {
            "name": "Barcelona",
            "value": "barcelona"
        },
        {
            "name": "Johannesburg",
            "value": "johannesburg"
        },
        {
            "name": "Saint Petersburg",
            "value": "saint-petersburg"
        },
        {
            "name": "Qingdao",
            "value": "qingdao"
        },
        {
            "name": "Dalian",
            "value": "dalian"
        },
        {
            "name": "Washington, D.C.",
            "value": "washington-dc"
        },
        {
            "name": "Yangon",
            "value": "yangon"
        },
        {
            "name": "Alexandria",
            "value": "alexandria"
        },
        {
            "name": "Jinan",
            "value": "jinan"
        },
        {
            "name": "Guadalajara",
            "value": "guadalajara"
        },
        {
            "name": "Sydney",
            "value": "sydney"
        },
        {
            "name": "Melbourne",
            "value": "melbourne"
        },
        {
            "name": "Montreal",
            "value": "montreal"
        },
        {
            "name": "Ankara",
            "value": "ankara"
        },
        {
            "name": "Recife",
            "value": "recife"
        },
        {
            "name": "Durban",
            "value": "durban"
        },
        {
            "name": "Porto Alegre",
            "value": "porto-alegre"
        },
        {
            "name": "Dusseldorf",
            "value": "dusseldorf"
        },
        {
            "name": "Hamburg",
            "value": "hamburg"
        },
        {
            "name": "Cape Town",
            "value": "cape-town"
        },
        {
            "name": "Stockholm",
            "value": "stockholm"
        }
    ]
</script>
<div class="m-multi-select js-m-multi-select" data-multi-select-name="cities">
	{{#if default_value}}
		<input type="hidden" name="cities[]" value="{{default_value}}">
	{{/if}}
	<div class="m-multi-select__search">
		<input type="search" class="a-input js-m-multi-select__input" data-multi-select-suggestions="suggestions" aria-expanded="false" aria-controls="multiSelectSuggestionsBox" aria-autocomplete="list" aria-haspopup="listbox" role="combobox" placeholder="Sök städer..." aria-label="Sök städer" aria-describedby="multiSelectDescription"/>
		<div class="m-multi-select__suggestions-box js-m-multi-select-suggestions-box" id="multiSelectSuggestionsBox" role="listbox" aria-live="assertive" aria-atomic="true"></div>
	</div>
	<ul class="m-multi-select-selected-items js-m-multi-select-selected-items" aria-live="assertive" aria-atomic="true" aria-multiselectable="true"></ul>
	<div class="u-visuallyhidden" id="multiSelectDescription">Använd upp och ner-pilarna för att lägga till sökträffar och tabb plus enter för att ta bort valda sökträffar</div>
</div>
{{#if add}}
	<form id="addMultiSelectItem" class="u-p-default background-snow">
		<input class="a-input" placeholder="Ange stad + enter">
	</form>
	<script>
		document.getElementById('addMultiSelectItem').addEventListener('submit', (e) => {
			e.preventDefault();
			const input = e.target.querySelector('input');
			const name = input.value;
			const select = document.querySelector('.js-m-multi-select');

			if (name) {
				select.multiSelect.addSuggestions([{ name, value: name }]);
				select.multiSelect.addItem({ name, value: name });
				input.value = '';
			}
		});
	</script>
{{/if}}

<script id="suggestions" type="application/json">
	[
		{
		"name":"New York",
		"value": "new-york"
		},
		{
		"name":"Los Angeles",
		"value": "los-angeles"
		},
		{
		"name":"Chicago",
		"value": "chicago"
		},
		{
		"name":"Houston",
		"value": "houston"
		},
		{
		"name":"Phoenix",
		"value": "phoenix"
		},
		{
		"name":"Philadelphia",
		"value": "philadelphia"
		},
		{
		"name":"San Antonio",
		"value": "san-antonio"
		},
		{
		"name":"San Diego",
		"value": "san-diego"
		},
		{
		"name":"Dallas",
		"value": "dallas"
		},
		{
		"name":"San Jose",
		"value": "san-jose"
		},
		{
		"name":"Austin",
		"value": "austin"
		},
		{
		"name":"Jacksonville",
		"value": "jacksonville"
		},
		{
		"name":"Fort Worth",
		"value": "fort-worth"
		},
		{
		"name":"Columbus",
		"value": "columbus"
		},
		{
		"name":"San Francisco",
		"value": "san-francisco"
		},
		{
		"name":"Tokyo",
		"value": "tokyo"
		},
		{
		"name":"Delhi",
		"value": "delhi"
		},
		{
		"name":"Shanghai",
		"value": "shanghai"
		},
		{
		"name":"Sao Paulo",
		"value": "sao-paulo"
		},
		{
		"name":"Mumbai",
		"value": "mumbai"
		},
		{
		"name":"Beijing",
		"value": "beijing"
		},
		{
		"name":"Cairo",
		"value": "cairo"
		},
		{
		"name":"Dhaka",
		"value": "dhaka"
		},
		{
		"name":"Mexico City",
		"value": "mexico-city"
		},
		{
		"name":"Osaka",
		"value": "osaka"
		},
		{
		"name":"Karachi",
		"value": "karachi"
		},
		{
		"name":"Chongqing",
		"value": "chongqing"
		},
		{
		"name":"Istanbul",
		"value": "istanbul"
		},
		{
		"name":"Buenos Aires",
		"value": "buenos-aires"
		},
		{
		"name":"Kolkata",
		"value": "kolkata"
		},
		{
		"name":"Kinshasa",
		"value": "kinshasa"
		},
		{
		"name":"Lagos",
		"value": "lagos"
		},
		{
		"name":"Manila",
		"value": "manila"
		},
		{
		"name":"Tianjin",
		"value": "tianjin"
		},
		{
		"name":"Rio de Janeiro",
		"value": "rio-de-janeiro"
		},
		{
		"name":"Guangzhou",
		"value": "guangzhou"
		},
		{
		"name":"Lahore",
		"value": "lahore"
		},
		{
		"name":"Moscow",
		"value": "moscow"
		},
		{
		"name":"Shenzhen",
		"value": "shenzhen"
		},
		{
		"name":"Bangalore",
		"value": "bangalore"
		},
		{
		"name":"Paris",
		"value": "paris"
		},
		{
		"name":"Bogota",
		"value": "bogota"
		},
		{
		"name":"Jakarta",
		"value": "jakarta"
		},
		{
		"name":"Chennai",
		"value": "chennai"
		},
		{
		"name":"Lima",
		"value": "lima"
		},
		{
		"name":"Bangkok",
		"value": "bangkok"
		},
		{
		"name":"Seoul",
		"value": "seoul"
		},
		{
		"name":"Nagoya",
		"value": "nagoya"
		},
		{
		"name":"Hyderabad",
		"value": "hyderabad"
		},
		{
		"name":"London",
		"value": "london"
		},
		{
		"name":"Tehran",
		"value": "tehran"
		},
		{
		"name":"Chengdu",
		"value": "chengdu"
		},
		{
		"name":"Nanjing",
		"value": "nanjing"
		},
		{
		"name":"Wuhan",
		"value": "wuhan"
		},
		{
		"name":"Ho Chi Minh City",
		"value": "ho-chi-minh-city"
		},
		{
		"name":"Luanda",
		"value": "luanda"
		},
		{
		"name":"Ahmedabad",
		"value": "ahmedabad"
		},
		{
		"name":"Kuala Lumpur",
		"value": "kuala-lumpur"
		},
		{
		"name":"Xi’an",
		"value": "xi-an"
		},
		{
		"name":"Hong Kong",
		"value": "hong-kong"
		},
		{
		"name":"Dongguan",
		"value": "dongguan"
		},
		{
		"name":"Hangzhou",
		"value": "hangzhou"
		},
		{
		"name":"Foshan",
		"value": "foshan"
		},
		{
		"name":"Shenyang",
		"value": "shenyang"
		},
		{
		"name":"Riyadh",
		"value": "riyadh"
		},
		{
		"name":"Baghdad",
		"value": "baghdad"
		},
		{
		"name":"Santiago",
		"value": "santiago"
		},
		{
		"name":"Surat",
		"value": "surat"
		},
		{
		"name":"Madrid",
		"value": "madrid"
		},
		{
		"name":"Suzhou",
		"value": "suzhou"
		},
		{
		"name":"Pune",
		"value": "pune"
		},
		{
		"name":"Harbin",
		"value": "harbin"
		},
		{
		"name":"Houston",
		"value": "houston"
		},
		{
		"name":"Toronto",
		"value": "toronto"
		},
		{
		"name":"Dar es Salaam",
		"value": "dar-es-salaam"
		},
		{
		"name":"Miami",
		"value": "miami"
		},
		{
		"name":"Belo Horizonte",
		"value": "belo-horizonte"
		},
		{
		"name":"Singapore",
		"value": "singapore"
		},
		{
		"name":"Atlanta",
		"value": "atlanta"
		},
		{
		"name":"Fukuoka",
		"value": "fukuoka"
		},
		{
		"name":"Khartoum",
		"value": "khartoum"
		},
		{
		"name":"Barcelona",
		"value": "barcelona"
		},
		{
		"name":"Johannesburg",
		"value": "johannesburg"
		},
		{
		"name":"Saint Petersburg",
		"value": "saint-petersburg"
		},
		{
		"name":"Qingdao",
		"value": "qingdao"
		},
		{
		"name":"Dalian",
		"value": "dalian"
		},
		{
		"name":"Washington, D.C.",
		"value": "washington-dc"
		},
		{
		"name":"Yangon",
		"value": "yangon"
		},
		{
		"name":"Alexandria",
		"value": "alexandria"
		},
		{
		"name":"Jinan",
		"value": "jinan"
		},
		{
		"name":"Guadalajara",
		"value": "guadalajara"
		},
		{
		"name":"Sydney",
		"value": "sydney"
		},
		{
		"name":"Melbourne",
		"value": "melbourne"
		},
		{
		"name":"Montreal",
		"value": "montreal"
		},
		{
		"name":"Ankara",
		"value": "ankara"
		},
		{
		"name":"Recife",
		"value": "recife"
		},
		{
		"name":"Durban",
		"value": "durban"
		},
		{
		"name":"Porto Alegre",
		"value": "porto-alegre"
		},
		{
		"name":"Dusseldorf",
		"value": "dusseldorf"
		},
		{
		"name":"Hamburg",
		"value": "hamburg"
		},
		{
		"name":"Cape Town",
		"value": "cape-town"
		},
		{
		"name":"Stockholm",
		"value": "stockholm"
		}
	]
</script>
{
  "default_value": null,
  "add": false
}
  • Content:
    @charset "UTF-8";
    @use '../../configurations/mixins' as mixin;
    @use '../../configurations/extends';
    @use '../../configurations/bem' as bem;
    @use '../../configurations/config' as config;
    @use '../../configurations/variables' as var;
    @use '../../configurations/functions' as func;
    @use '../../configurations/colors/colors' as colors;
    @use '../../configurations/colors/colors-functions' as colorFunc;
    @use '../../vendor/grid/breakpoints' as breakpoint;
    @use '../../vendor/grid/grid' as grid;
    @use '../../utilities/hide';
    
    @include mixin.molecule(multi-select) {
    	position: relative;
    
    	&::before {
    		@extend %u-visuallyhidden;
    
    		content: var.$namespace;
    	}
    
    	@include bem.e(search) {
    		position: relative;
    	}
    
    	@include bem.e(suggestions-box) {
    		position: absolute;
    		border-top: none;
    		z-index: func.z_index(foreground);
    		top: 100%;
    		left: 0;
    		right: 0;
    		background-color: colors.$color-snow;
    		overflow-y: auto;
    		max-height: func.rhythm(15);
    		border-bottom-left-radius: var.$border-radius;
    		border-bottom-right-radius: var.$border-radius;
    
    		@extend %box-shadow;
    	}
    
    	@include bem.e(suggestion-btn) {
    		padding: func.rhythm(1);
    		cursor: pointer;
    		background-color: colors.$color-snow;
    		border: none;
    		border-bottom: 1px solid colors.$color-concrete;
    		width: 100%;
    		text-align: left;
    
    		&:hover,
    		&.autocomplete-active {
    			background-color: colors.$color-ocean-dark;
    			color: colors.$color-snow;
    		}
    	}
    
    	@include bem.e(tag){
    		margin-bottom: func.rhythm(1);
    		background-color: colors.$color-ash;
    		text-transform: none;
    		font-size: var.$size-medium;
    
    		&:hover,
    		&:focus {
    			background-color: colors.$color-ash;
    			color: colors.$color-cyberspace;
    		}
    	}
    }
    
    /* Selected items container */
    @include mixin.molecule(multi-select-selected-items) {
    	margin-top: -#{func.rhythm(1)};
    	padding: func.rhythm(2) func.rhythm(1) 0 func.rhythm(1);
    	border: 1px solid #d4d4d4;
    	background-color: colors.$color-concrete;
    	border-bottom-left-radius: var.$border-radius;
    	border-bottom-right-radius: var.$border-radius;
    
    	&:empty {
    		display: none;
    	}
    
    	@include bem.e(remove-btn) {
    		margin-left: 5px;
    		border: none;
    		background-color: #d80000;
    		color: white;
    		border-radius: 50%;
    		cursor: pointer;
    		display: flex;
    		align-items: center;
    		justify-content: center;
    		line-height: 1;
    		width: var.$icon-size-small;
    		height: var.$icon-size-small;
    		position: relative;
    		transform: translateX(func.rhythm(0.5));
    
    		&::after {
    			content: '';
    			display: block;
    			width: 100%;
    			height: 100%;
    			position: absolute;
    			top: 0;
    			right: 0;
    			bottom: 0;
    			left: 0;
    			background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI4LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxNiAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTYgMTY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRkZGRkZGO30KPC9zdHlsZT4KPHBvbHlnb24gY2xhc3M9InN0MCIgcG9pbnRzPSI4LDEwIDIsMTYgMCwxNCA2LDggMCwyIDIsMCA4LDYgMTQsMCAxNiwyIDEwLDggMTYsMTQgMTQsMTYgIi8+Cjwvc3ZnPgo=);
    			background-repeat: no-repeat;
    			background-position: center center;
    			background-size: calc(var.$icon-size-small / 2) calc(var.$icon-size-small / 2);
    		}
    
    		&:hover,
    		&:focus {
    			background-color: colors.$color-cyberspace;
    		}
    	}
    }
    
  • URL: /components/raw/multi-select/_multi-select.scss
  • Filesystem Path: src/molecules/multi-select/_multi-select.scss
  • Size: 3.6 KB
  • Content:
    import className from '../../assets/js/className';
    
    class MultiSelect {
    	constructor(el) {
    		this.element = el;
    		this.baseClassName = 'm-multi-select';
    		this.currentFocus = -1;
    		this.name = this.element.getAttribute('data-multi-select-name');
    		this.input = this.element.querySelector(`.js-${this.baseClassName}__input`);
    		this.suggestionsBox = this.element.querySelector(`.js-${this.baseClassName}-suggestions-box`);
    		this.selectedItemsList = this.element.querySelector('.js-m-multi-select-selected-items');
    		this.selectedItems = [];
    		this.data = [];
    
    		this.getData();
    		this.attach();
    		this.sync();
    	}
    
    	getData() {
    		const id = this.input.getAttribute('data-multi-select-suggestions');
    		const el = document.getElementById(id);
    
    		if (!el) {
    			this.data = [];
    			return;
    		}
    
    		this.data = JSON.parse(el.textContent);
    	}
    
    	attach() {
    		this.input.addEventListener('input', this.onInput);
    		this.input.addEventListener('keydown', this.onKeyDown);
    		this.suggestionsBox.addEventListener('click', this.onClick);
    	}
    
    	setFocus(index) {
    		this.currentFocus = index;
    	}
    
    	resetFocus() {
    		this.setFocus(-1);
    	}
    
    	clearSuggestions() {
    		this.input.setAttribute('aria-expanded', 'false');
    		this.suggestionsBox.innerHTML = '';
    	}
    
    	addSuggestions(suggestions) {
    		this.data = [
    			...this.data,
    			...suggestions.filter((s) => !this.data.find((d) => d.value === s.value)),
    		];
    	}
    
    	sync() {
    		const inputs = this.element.querySelectorAll(`input[name="${this.name}[]"]`);
    
    		this.selectedItems = [];
    
    		inputs.forEach((input) => {
    			const item = this.data.find((d) => d.value === input.value);
    
    			if (item) {
    				this.addItem(item, false);
    			}
    		});
    	}
    
    	filterData(query) {
    		const selectedValues = this.selectedItems.map((item) => item.value);
    
    		return this.data
    			.filter((item) => item.name.toLowerCase().startsWith(query.toLowerCase()))
    			.filter((item) => !selectedValues.includes(item.value));
    	}
    
    	populateSuggestions(suggestions) {
    		const cls = className(`${this.baseClassName}__suggestion-btn`);
    		this.suggestionsBox.innerHTML = suggestions.map((item) => `<button class='${cls}' tabindex='0' value="${item.value}">${item.name}</button>`).join('');
    
    		if (this.suggestionsBox.innerHTML) {
    			this.input.setAttribute('aria-expanded', 'true');
    		}
    	}
    
    	onInput = () => {
    		const { value } = this.input;
    
    		// Clear suggestions if less than 2 characters are typed
    		if (value.length < 2) {
    			this.clearSuggestions();
    
    			return;
    		}
    
    		const suggestions = this.filterData(value);
    
    		if (suggestions.length) {
    			this.populateSuggestions(suggestions);
    		} else {
    			this.clearSuggestions();
    		}
    
    		this.resetFocus();
    	};
    
    	removeItem(item) {
    		const node = this.element.querySelector(`.js-m-multi-select-selected-items li[data-value="${item.value}"]`);
    
    		if (!node) {
    			return;
    		}
    
    		const parent = node.closest('.js-m-multi-select-selected-items');
    		const index = Array.prototype.indexOf.call(parent.children, node);
    
    		node.remove();
    
    		const remainingItems = parent.getElementsByTagName('li');
    
    		// Focus management: set focus to the next item, or the search input if no items left
    		if (remainingItems.length > 0) {
    			if (index < remainingItems.length) {
    				remainingItems[index].getElementsByTagName('button')[0].focus();
    			} else {
    				remainingItems[remainingItems.length - 1].getElementsByTagName('button')[0].focus();
    			}
    		} else {
    			this.input.focus();
    		}
    
    		this.selectedItems = this.selectedItems
    			.filter((i) => i.value !== item.value);
    
    		const itemInput = this.element.querySelector(`input[value="${item.value}"]`);
    		itemInput.remove();
    	}
    
    	addItem(item, save = true) {
    		if (!item) {
    			return;
    		}
    
    		const newItem = document.createElement('li');
    		newItem.textContent = `${item.name} `;
    		newItem.setAttribute('data-value', item.value);
    		newItem.classList.add(className('a-tag'));
    		newItem.classList.add(className(`${this.baseClassName}__tag`));
    
    		const removeBtn = document.createElement('button');
    		removeBtn.classList.add(className(`${this.baseClassName}-selected-items__remove-btn`));
    
    		const buttonTextContainer = document.createElement('span');
    		buttonTextContainer.classList.add('u-visuallyhidden');
    		removeBtn.appendChild(buttonTextContainer);
    		buttonTextContainer.textContent = `Ta bort ${item.name}`; // Accessibility label for screen readers
    
    		// Event listener for removing the selected item
    		removeBtn.addEventListener('click', () => {
    			this.removeItem(item);
    		});
    
    		newItem.appendChild(removeBtn);
    
    		this.selectedItemsList.appendChild(newItem);
    		this.selectedItems.push(item);
    
    		if (save) {
    			const itemInput = document.createElement('input');
    
    			itemInput.type = 'hidden';
    			itemInput.name = `${this.name}[]`;
    			itemInput.value = item.value;
    
    			this.element.appendChild(itemInput);
    		}
    	}
    
    	removeHighlight() {
    		const items = this.suggestionsBox.getElementsByClassName(className(`${this.baseClassName}__suggestion-btn`));
    
    		[].forEach.call(items, (item) => {
    			item.classList.remove('autocomplete-active');
    		});
    	}
    
    	highlight(direction) {
    		const items = this.suggestionsBox.getElementsByClassName(className(`${this.baseClassName}__suggestion-btn`));
    		let focus = this.currentFocus;
    
    		if (direction === 'down') {
    			focus = (focus >= items.length - 1) ? 0 : focus + 1;
    		} else {
    			focus = (focus <= 0) ? items.length - 1 : focus - 1;
    		}
    
    		this.setFocus(focus);
    		this.removeHighlight();
    
    		items[this.currentFocus].classList.add('autocomplete-active');
    		items[this.currentFocus].scrollIntoView({ block: 'nearest' });
    	}
    
    	selectHighlighted() {
    		const items = this.suggestionsBox.getElementsByClassName(className(`${this.baseClassName}__suggestion-btn`));
    
    		if (this.currentFocus > -1 && items[this.currentFocus]) {
    			const item = items[this.currentFocus];
    
    			this.addItem(this.data.find((d) => d.value === item.value));
    
    			this.clearSuggestions();
    			this.input.value = '';
    			this.resetFocus();
    		}
    	}
    
    	onKeyDown = (e) => {
    		if (e.keyCode === 40) {
    			this.highlight('down');
    		} else if (e.keyCode === 38) {
    			this.highlight('up');
    		} else if (e.keyCode === 13) {
    			e.preventDefault();
    
    			this.selectHighlighted();
    		}
    	};
    
    	onClick = (e) => {
    		if (e.target.classList.contains(className(`${this.baseClassName}__suggestion-btn`))) {
    			this.addItem(this.data.find((d) => d.value === e.target.value));
    			this.clearSuggestions();
    			this.input.value = '';
    		}
    	};
    }
    
    const multiSelectElements = document.querySelectorAll('.js-m-multi-select');
    
    if (multiSelectElements) {
    	[].forEach.call(multiSelectElements, (el) => {
    		el.multiSelect = new MultiSelect(el);
    	});
    }
    
  • URL: /components/raw/multi-select/multi-select.js
  • Filesystem Path: src/molecules/multi-select/multi-select.js
  • Size: 6.6 KB

Accessible Multi Select with Autocomplete

Use your mouse or keyboard to select and remove items. Compatible with screen readers.