Reputation: 77
I have an international custom dropdown country code selector section in my project and the code is working fine, but the problem I am facing right now is that when a user opens the dropdown list second time, then I am unable to get the first selected option highlighted using css.
That means, when a user selects an option for the first time on the dropdown, I want that option to be highlighted using the background color property. So that when user re-open the dropdown menu second time so that they should see the selected option as highlighted.
const countryData = [
{ name: "Afganistan", code: "93", flag: "afg" },
{ name: "Albania", code: "355", flag: "alb" },
{ name: "Algeria", code: "213", flag: "dza" },
{ name: "Canada", code: "1", flag: "can" },
{ name: "Cape Verde", code: "238", flag: "cpv" },
{ name: "Cayman Islands", code: "1-345", flag: "cym" },
{ name: "United States", code: "1", flag: "usa" },
{ name: "Uruguay", code: "598", flag: "ury" },
{ name: "Uzbekistan", code: "998", flag: "uzb" }
];
function registerForm(formRoot) {
const ccButton = formRoot.querySelector(".cc-telcode");
const ccContainer = formRoot.querySelector(".cc-container");
const ccSearchInput = formRoot.querySelector(".cc-search-box");
const ccList = formRoot.querySelector(".cc-data-list");
let selectedCountry = ""; // saved by "code"
// Add event listeners to the button, input, and list
// We use a process called "event delegation" on the list
// to catch events from its children as they "bubble up" the DOM
// https://dmitripavlutin.com/javascript-event-delegation/
ccButton.addEventListener("click", handleButton);
ccSearchInput.addEventListener("input", handleInput);
ccList.addEventListener("click", handleListClick);
document.addEventListener("click", handleDocumentClick);
// Handles the document click - it checks to see if the clicked
// part of the document has a parent element which is either
// `null` or is the HTML element, and then closes the container
// if it's open
function handleDocumentClick(e) {
const { parentElement } = e.target;
document.addEventListener("click", (event) => {
if (!formRoot.contains(event.target)) {
ccContainer.classList.remove("show-cc-list");
}
});
}
// Toggle the container on/off
function handleButton() {
ccContainer.classList.toggle("show-cc-list");
ccList.innerHTML = createListHtml(countryData);
}
// Create a series of list items based on the
// data passed to it
function createListHtml(countryData) {
return countryData.map((obj) => {
const { name, code, flag } = obj;
let isSelected = "";
if (obj.code == selectedCountry) isSelected = "selected-country";
return `
<li class="cc-list-items ${isSelected}" data-name="${name}" data-code="${code}" data-flag="${flag}">
<div class="flag-icon flag-icon-${flag}"></div>
<div class="name">${name} (+${code})</div>
</li>
`;
}).join("");
}
// When the input is changed filter the data
// according to the current value, and then
// create some list items using that filtered data
function handleInput(e) {
const { value } = e.target;
if (value) {
const filtered = filterData(countryData, value);
if (filtered.length) {
ccList.innerHTML = createListHtml(filtered);
} else {
ccList.innerHTML = createNoDataHtml();
}
} else {
ccList.innerHTML = createListHtml(countryData);
}
}
// When an item is clicked, grab the relevant data
// attributes, create the new button HTML, and then
// close the container
function handleListClick(e) {
const item = e.target.closest("li") || e.target;
if (item.classList.contains("cc-list-items")) {
const { code, flag } = item.dataset;
selectedCountry = item.dataset.code;
ccButton.innerHTML = createButtonHtml(code, flag);
ccContainer.classList.remove("show-cc-list");
}
}
}
// Filters the data based on the characters
// at the start of the provided name
function filterData(countryData, value) {
return countryData.filter((obj) => {
return (
obj.name.toLowerCase().startsWith(value.toLowerCase()) ||
obj.code.toLowerCase().startsWith(value.toLowerCase())
);
});
}
// Create some button HTML
function createButtonHtml(code, flag) {
return `
<div class="flag-icon flag-icon-${flag}"></div>
<option class="cc-code" value="+${code}">+${code}</option>
`;
}
// No data available list item
function createNoDataHtml() {
return '<li class="no-data-found">Sorry, No Data Found</li>';
}
const contactDiv = document.querySelector(".contact-frm-cc");
registerForm(contactDiv);
.cc-telcode {
margin-bottom: 1em;
display: flex;
justify-content: center;
width: 100%;
}
.cc-telcode div.cc-code,
.cc-list-items div.name {
margin-left: 0.25em;
}
.cc-container {
display: none;
width: 300px;
position: absolute;
}
.show-cc-list {
display: block;
z-index: +999;
}
.cc-data-list {
max-height: 100px;
list-style: none;
margin: 1em 0 0 0;
padding: 0;
overflow-y: scroll;
border: 1px soldi darkgray;
}
.cc-list-items {
display: flex;
padding: 0.25em;
border: 1px solid lightgray;
}
.cc-list-items:hover {
cursor: pointer;
background-color: lightyellow;
}
.selected-country {
cursor: pointer;
background-color: rgb(73, 118, 241);
}
.contact-frm-cc,
.consult-frm-cc {
width: 100px;
}
<link href="https://amitdutta.co.in/flag/css/flag-icon.css" rel="stylesheet"/>
<div class="contact-frm-cc">
<button class="cc-telcode">Tel code</button>
<section class="cc-container">
<input type="text" class="cc-search-box" placeholder="Search for country" />
<ul class="cc-data-list">
</ul>
</section>
</div>
Upvotes: 1
Views: 159
Reputation: 50
What i changed:
The list wont be created anymore when the page is loaded, it recreates every single time, when the user clicks on the button to open the list. (line 119)
When you click on a country, it saves its "code" into a variable (line 6).
inside function createListHtml() it matches the saved code with every single county, if match, then it gives the entry an id named "selected" (i gave it a yellow background in the css)
I hope you can understand what i wrote :)
Heres the code (i didnt changed the html file) : Css:
.telcode { margin-bottom: 1em; display: flex;}
.telcode div.code, .item div.name { margin-left: 0.25em; }
.container { display: none; }
.show { display: block; }
.list { height: 100px; list-style: none; margin: 1em 0 0 0; padding: 0; overflow-y: scroll; border: 1px soldi darkgray; }
.item { display: flex; padding: 0.25em; border: 1px solid lightgray; }
.item:hover { cursor: pointer; background-color: lightyellow; }
#selected { cursor: pointer; background-color: yellow; }
Js:
const countryData = [
{ name: "Afganistan", code: "93", flag: "afg" },
{ name: "Albania", code: "355", flag: "alb" },
{ name: "Algeria", code: "213", flag: "dza" },
{ name: "Canada", code: "1", flag: "can" },
{ name: "Cape Verde", code: "238", flag: "cpv" },
{ name: "Cayman Islands", code: "1-345", flag: "cym" },
{ name: "United States", code: "1", flag: "usa" },
{ name: "Uruguay", code: "598", flag: "ury" },
{ name: "Uzbekistan", code: "998", flag: "uzb" }
];
function registerForm(formRoot) {
const ccButton = formRoot.querySelector(".cc-telcode");
const ccContainer = formRoot.querySelector(".cc-container");
const ccSearchInput = formRoot.querySelector(".cc-search-box");
const ccList = formRoot.querySelector(".cc-data-list");
let selectedCountry = ""; // saved by "code"
// Add event listeners to the button, input, and list
// We use a process called "event delegation" on the list
// to catch events from its children as they "bubble up" the DOM
// https://dmitripavlutin.com/javascript-event-delegation/
ccButton.addEventListener("click", handleButton);
ccSearchInput.addEventListener("input", handleInput);
ccList.addEventListener("click", handleListClick);
document.addEventListener("click", handleDocumentClick);
// Handles the document click - it checks to see if the clicked
// part of the document has a parent element which is either
// `null` or is the HTML element, and then closes the container
// if it's open
function handleDocumentClick(e) {
const { parentElement } = e.target;
document.addEventListener("click", (event) => {
if (!formRoot.contains(event.target)) {
ccContainer.classList.remove("show-cc-list");
}
});
}
// Toggle the container on/off
function handleButton() {
ccContainer.classList.toggle("show-cc-list");
ccList.innerHTML = createListHtml(countryData);
}
// Create a series of list items based on the
// data passed to it
function createListHtml(countryData) {
return countryData.map((obj) => {
const { name, code, flag } = obj;
let isSelected = "";
if (obj.name == selectedCountry) isSelected = "selected-country";
return `
<li class="cc-list-items ${isSelected}" data-name="${name}" data-code="${code}" data-flag="${flag}">
<div class="flag-icon flag-icon-${flag}"></div>
<div class="name">${name} (+${code})</div>
</li>
`;
}).join("");
}
// When the input is changed filter the data
// according to the current value, and then
// create some list items using that filtered data
function handleInput(e) {
const { value } = e.target;
if (value) {
const filtered = filterData(countryData, value);
if (filtered.length) {
ccList.innerHTML = createListHtml(filtered);
} else {
ccList.innerHTML = createNoDataHtml();
}
} else {
ccList.innerHTML = createListHtml(countryData);
}
}
// When an item is clicked, grab the relevant data
// attributes, create the new button HTML, and then
// close the container
function handleListClick(e) {
const item = e.target.closest("li") || e.target;
if (item.classList.contains("cc-list-items")) {
const { code, flag } = item.dataset;
selectedCountry = item.dataset.name;
ccButton.innerHTML = createButtonHtml(code, flag);
ccContainer.classList.remove("show-cc-list");
}
}
}
// Filters the data based on the characters
// at the start of the provided name
function filterData(countryData, value) {
return countryData.filter((obj) => {
return (
obj.name.toLowerCase().startsWith(value.toLowerCase()) ||
obj.code.toLowerCase().startsWith(value.toLowerCase())
);
});
}
// Create some button HTML
function createButtonHtml(code, flag) {
return `
<div class="flag-icon flag-icon-${flag}"></div>
<option class="cc-code" value="+${code}">+${code}</option>
`;
}
// No data available list item
function createNoDataHtml() {
return '<li class="no-data-found">Sorry, No Data Found</li>';
}
const contactDiv = document.querySelector(".contact-frm-cc");
registerForm(contactDiv);
Hope this helps! Enjoy coding!
Upvotes: 1
Reputation: 5571
You could use the .setAttribute()
method to apply an inline style to the selected li
element.
Like this:
item.setAttribute("style", "background-color: lightyellow;");
Before, you'll need to reset any previously highlighted element with this method:
function resetSelection() {
let items = document.querySelectorAll("li[style]"), item;
for (let i = 0; i < items.length; i++) {
item = items[i];
item.removeAttribute("style");
}
}
See the full example below:
// Cache the elements
const button = document.querySelector('.telcode');
const container = document.querySelector('.container');
const input = document.querySelector('input');
const list = document.querySelector('.list');
// Add event listeners to the button, input, and list
// We use a process called "event delegation" on the list
// to catch events from its children as they "bubble up" the DOM
// https://dmitripavlutin.com/javascript-event-delegation/
button.addEventListener('click', handleButton);
input.addEventListener('input', handleInput);
list.addEventListener('click', handleListClick);
document.addEventListener('click', handleDocumentClick);
// Handles the document click - it checks to see if the clicked
// part of the document has a parent element which is either
// `null` or is the HTML element, and then closes the container
// if it's open
function handleDocumentClick(e) {
const {
parentElement
} = e.target;
if (!parentElement || parentElement.nodeName === 'HTML') {
if (container.classList.contains('show')) {
container.classList.remove('show');
}
}
}
// All of the data held as objects within an array
const data = [{
name: 'Afganistan',
code: '69',
flag: 'afg'
},
{
name: 'Barbados',
code: '1-246',
flag: 'brb'
},
{
name: 'Bolivia',
code: '591',
flag: 'bol'
},
{
name: 'Cuba',
code: '53',
flag: 'cub'
},
{
name: 'Fiji',
code: '679',
flag: 'fji'
},
];
// Filters the data based on the characters
// at the start of the provided name
function filterData(data, value) {
return data.filter(obj => {
return (
obj.name.toLowerCase().startsWith(value.toLowerCase()) ||
obj.code.toLowerCase().startsWith(value.toLowerCase())
);
});
}
// Create a series of list items based on the
// data passed to it
function createListHtml(data) {
return data.map(obj => {
const {
name,
code,
flag
} = obj;
return `
<li
class="item"
data-name="${name}"
data-code="${code}"
data-flag="${flag}"
>
<div class="flag-icon flag-icon-${flag}"></div>
<div class="name">${name} (+${code})</div>
</li>
`;
}).join('');
}
// Toggle the container on/off
function handleButton() {
container.classList.toggle('show');
}
// No data available list item
function createNoDataHtml() {
return '<li class="nodata">No data available</li>';
}
// When the input is changed filter the data
// according to the current value, and then
// create some list items using that filtered data
function handleInput(e) {
const {
value
} = e.target;
if (value) {
const filtered = filterData(data, value);
if (filtered.length) {
list.innerHTML = createListHtml(filtered);
} else {
list.innerHTML = createNoDataHtml();
}
} else {
list.innerHTML = createListHtml(data);
}
}
// Create some button HTML
function createButtonHtml(code, flag) {
return `
<div class="flag-icon flag-icon-${flag}"></div>
<div class="code">+${code}</div>
`;
}
// When an item is clicked, grab the relevant data
// attributes, create the new button HTML, and then
// close the container
function handleListClick(e) {
const item = e.target.closest('li') || e.target;
if (item.classList.contains('item')) {
const {
code,
flag
} = item.dataset;
button.innerHTML = createButtonHtml(code, flag);
container.classList.remove('show');
resetSelection();
item.setAttribute("style", "background-color: lightyellow;");
}
}
function resetSelection() {
let items = document.querySelectorAll("li[style]"), item;
for (let i = 0; i < items.length; i++) {
item = items[i];
item.removeAttribute("style");
}
}
list.innerHTML = createListHtml(data);
.telcode {
margin-bottom: 1em;
display: flex;
}
.telcode div.code,
.item div.name {
margin-left: 0.25em;
}
.container {
display: none;
}
.show {
display: block;
}
.list {
height: 100px;
list-style: none;
margin: 1em 0 0 0;
padding: 0;
overflow-y: scroll;
border: 1px soldi darkgray;
}
.item {
display: flex;
padding: 0.25em;
border: 1px solid lightgray;
}
.item:hover {
cursor: pointer;
background-color: lightyellow;
}
<link href="https://amitdutta.co.in/flag/css/flag-icon.css" rel="stylesheet" />
<button type="button" class="telcode">Tel code</button>
<section class="container">
<input type="text" placeholder="Search for country" />
<ul class="list"></ul>
</section>
Hope this helps!
Merry Christmas! ✨❤✨
Upvotes: 1