- Published on
- ยท11 min read
Controlling Multiple Select Boxes Simultaneously with a Single Set of Data (JavaScript)
I apologize in advance for any awkward expressions in English.
English is not my native language, and I have relied on ChatGPT's assistance to proceed with the translation.
Explanation
In the Aspect Ratio Comparison Service, the -- Ratio --
, -- Resolution --
, and -- Usage --
select boxes all utilize the same data source.
When you select an aspect ratio in one of these select boxes, the corresponding aspect ratios in the other select boxes are automatically disabled.
For instance, if you select Instagram - 1:1
in the -- Usage --
select box, the 1:1
in the -- Ratio --
select box and 1080x1080 - 1:1
in the -- Resolution --
select box will be disabled simultaneously.
Please refer to the GIF image below:
Pre-Entry Notes
Here is a source code shared for the purpose of explanation, which has undergone some minor edits. Some parts that were split into functions in the actual source code have been combined, and unnecessary code for this feature has been omitted. Please note that there may be redundant code due to comments for explanation.
Implementation
1. Define Data for Each Select Box
Define the data for aspect ratios, usages, and resolutions, each with its own key.
The code is as follows:
const AspectRatioInfo = function () {
const originalRatioData = [
{
key: '1', // Used as the key and as the value for select > option
width: 1, // Used for aspect ratio calculation
height: 1, // Used for aspect ratio calculation
text: '1:1', // Displayed in the select box
range: [1], // To find directly calculated ratios within this range
usages: ['Instagram'], // List of use cases (an array)
resolutions: ['1080x1080'], // List of resolutions (an array)
},
{
key: '1.33',
width: 4,
height: 3,
text: '4:3 (1.33:1)',
range: [1.33],
usages: ['iPad 5/6/7/8/9', 'iPad Mini 2/3/4/5', 'iPad Air 1/2/3', 'iPad Pro 12.9'],
resolutions: ['800x600', '1024x768', '1600x1200', '2048x1536'],
},
{
key: '1.77',
width: 16,
height: 9,
text: '16:9 (1.77:1)',
range: [1.77, 1.78],
usages: ['iPhone 5/6/7/8', 'iMac', 'Galaxy Note 5/9', 'YouTube'],
resolutions: [
'640x360',
'1280x720',
'1600x900',
'1920x1080',
'2560x1440',
'3200x1800',
'3840x2160',
'5120x2880',
'7680x4320',
],
},
// ... and so on ...
]
// ... Function for Data Filtering (explained below) ...
}
2. Filtering Functions for Data Display in Each Select Box
const AspectRatioInfo = function () {
// ... Declaration of originalRatioData, as explained above, is omitted ...
// Data sorting for the '-- Ratio --' select box
const getListOfWidthHeight = function () {
const listOfWidthHeight = originalRatioData
.sort(function (a, b) {
return a.width - b.width
})
.map(function (ratio) {
return {
key: ratio.key,
width: ratio.width,
height: ratio.height,
text: ratio.text,
ratio: ratio.range[0],
}
})
return listOfWidthHeight
}
// Data filtering and sorting for the '-- Resolution --' select box
const getListOfResolution = function () {
const resolutionArray = []
// Utilizing the 'resolutions' array in originalRatioData
originalRatioData.forEach(function (ratio) {
ratio.resolutions.forEach(function (resolution) {
resolutionArray.push({
key: ratio.key,
resolution: resolution,
text: ratio.text,
})
})
})
return resolutionArray.sort(function (a, b) {
const [a1, a2] = a.resolution.split('x').map(Number)
const [b1, b2] = b.resolution.split('x').map(Number)
return a1 - b1 || a2 - b2
})
}
// Data filtering and sorting for the '-- Usage --' select box
const getListOfUsage = function () {
const usageArray = []
// Utilizing the 'usages' array in originalRatioData
originalRatioData.forEach(function (ratio) {
ratio.usages.forEach(function (usage) {
usageArray.push({
key: ratio.key,
usage: usage,
text: ratio.text,
})
})
})
return usageArray.sort(function (a, b) {
return a.usage.localeCompare(b.usage)
})
}
}
3. Creating Select Boxes
<select id="selRatio" class="input-select-short">
<option value="">-- Ratio --</option>
</select>
<select id="selResolution" class "input-select-short">
<option value="">-- Resolution --</option>
</select>
<select id="selUsage" class="input-select-short">
<option value="">-- Usage --</option>
</select>
const aspectRatioInfo = new AspectRatioInfo()
window.addEventListener('load', function () {
setSelectBox()
})
function setSelectBox() {
// Select Box -- Ratio --
createSelectBox(document.getElementById('selRatio'), aspectRatioInfo.getListOfWidthHeight())
// Select Box -- Resolution --
createSelectBox(document.getElementById('selResolution'), aspectRatioInfo.getListOfResolution())
// Select Box -- Usage --
createSelectBox(document.getElementById('selUsage'), aspectRatioInfo.getListOfUsage())
}
// Creating Select Boxes
function createSelectBox(element, list) {
list.forEach(function (item) {
const optionElement = document.createElement('option')
optionElement.value = item.key
// Parts where the displayed text content should vary based on each select box
if (element.id === 'selResolution') {
optionElement.innerHTML = `${item.resolution} - ${item.text}`
} else if (element.id === 'selUsage') {
optionElement.innerHTML = `${item.usage} - ${item.text}`
} else {
optionElement.innerHTML = item.text
}
element.appendChild(optionElement)
})
}
4. Disabling the Corresponding Ratio in All Select Boxes When Selected in One
const AspectRatioInfo = function () {
const getRatioInfoByKey = function (key) {
const ratioInfo = originalRatioData.find(function (ratio) {
return ratio.key === key
})
return ratioInfo
}
}
// ---------------------------------
const aspectRatioInfo = new AspectRatioInfo()
let selectedAspectRatiosState = [] // Selected aspect ratio
// Event handling when a select box is chosen
window.addEventListener('load', function () {
document.addEventListener('change', function (event) {
if (event.target.tagName.toLowerCase() === 'select') {
const selectKey = event.target.value
// Retrieve data for the selected key and add it to selectedAspectRatiosState
const info = aspectRatioInfo.getRatioInfoByKey(selectKey)
info && addSelectedAspectRatiosState(info)
event.target.value = ''
}
})
})
// If there is already a selected ratio, return; otherwise, add it
function addSelectedAspectRatiosState(info) {
if (selectedAspectRatiosState.find((item) => item.key === info.key)) {
return
}
setSelectedAspectRatiosState([...selectedAspectRatiosState, info])
}
// Add the selected ratio and call the disable function
function setSelectedAspectRatiosState(newState) {
selectedAspectRatiosState = newState
selectedKeyDisabled()
}
// Disable the selected ratios
function selectedKeyDisabled() {
const optionElementList = document.querySelectorAll('option')
optionElementList.forEach(function (optionElement) {
if (
selectedAspectRatiosState.find(function (selectedKey) {
return selectedKey.key === optionElement.value
})
) {
optionElement.disabled = true
} else {
optionElement.disabled = false
}
})
}