Logo
Published on
·10 min read

하나의 데이터로 여러개의 select box 동시 제어(javascript)

설명할 내용

화면 비율(Aspect Ratio) 비교 서비스의 경우
-- 비율 --, -- 해상도 --, -- 사용처 --선택박스는 동일한 객체(데이터)를 사용합니다.

위 선택 박스 중 한 곳에서 비율을 선택하면 다른 선택 박스의 비율도 자동으로 disable 하도록 처리했습니다.

예를 들어, -- 사용처 --Instagram - 1:1 을 선택한 경우
-- 비율 --1:1-- 해상도 --1080x1080 - 1:1 이 함께 disable 처리됩니다.

아래 GIF 이미지를 참고하세요.

img

들어가기 전 참고

설명을 위해 약간의 편집이 들어간 소스 공유입니다.

실제 소스에서 함수로 쪼개진 부분을 합치기도 하고, 해당 기능에 필요 없는 소스는 생략하기도 했습니다. 설명을 위해 주석과 중복으로 코드가 들어간 부분도 있다는 점 참고하시길 바랍니다.

구현

1. 각 선택박스에 사용할 데이터 정의

비율 정보와, 사용처 목록(usages), 해상도 목록(resolutions)을 각각의 key로 정의합니다.

코드는 아래와 같습니다.

const AspectRatioInfo = function () {
  const originalRatioData = [
    {
      key: '1', // Key값으로 사용, select > option 의 value로 사용
      width: 1, // 비율 계산용
      height: 1, // 비율 계산용
      text: '1:1', // select box 표시용
      range: [1], // 직접 비율 계산시 해당 범위로 찾을 수 있도록 배열로
      usages: ['Instagram'], // 사용처 목록(배열)
      resolutions: ['1080x1080'], // 해상도 목록(배열)
    },
    {
      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',
      ],
    },
    // ... 생략 ...
  ]

  // ... 데이터 필터링 함수 내용으로 아래에서 설명하므로 생략 ...
}

2. 각 선택박스에 보여질 데이터 필터링 함수

const AspectRatioInfo = function () {
  // ... originalRatioData 선언 내용으로 위에서 설명됨. 생략 ...

  // -- 비율 -- 선택박스용 데이터 정렬
  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
  }

  // -- 해상도 -- 선택박스용 데이터 필터링 및 정렬
  const getListOfResolution = function () {
    const resolutionArray = []

    // originalRatioData > resolutions 배열 이용하기
    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
    })
  }

  // -- 사용처 -- 선택박스용 데이터 필터링 및 정렬
  const getListOfUsage = function () {
    const usageArray = []

    // originalRatioData > usages 배열 이용하기
    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. 선택 박스 생성

<select id="selRatio" class="input-select-short">
  <option value="">-- 비율 --</option>
</select>
<select id="selResolution" class="input-select-short">
  <option value="">-- 해상도 --</option>
</select>
<select id="selUsage" class="input-select-short">
  <option value="">-- 사용처 --</option>
</select>
const aspectRatioInfo = new AspectRatioInfo()

window.addEventListener('load', function () {
  setSelectBox()
})

function setSelectBox() {
  // 선택박스 -- 비율 --
  createSelectBox(document.getElementById('selRatio'), aspectRatioInfo.getListOfWidthHeight())
  // 선택박스 -- 해상도 --
  createSelectBox(document.getElementById('selResolution'), aspectRatioInfo.getListOfResolution())
  // 선택박스 -- 사용처 --
  createSelectBox(document.getElementById('selUsage'), aspectRatioInfo.getListOfUsage())
}

// 선택박스 생성
function createSelectBox(element, list) {
  list.forEach(function (item) {
    const optionElement = document.createElement('option')
    optionElement.value = item.key
    // 각 선택박스에 따라 보여지는 텍스트 내용이 달라져야 하는 부분
    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. 선택박스에서 선택 시 모든 선택박스에서 해당 비율 disable 처리

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 = [] // 선택된 aspect ratio

// 선택박스 선택 시 이벤트 처리
window.addEventListener('load', function () {
  document.addEventListener('change', function (event) {
    if (event.target.tagName.toLowerCase() === 'select') {
      const selectKey = event.target.value
      // 선택된 key의 데이터를 가져와 selectedAspectRatiosState에 추가
      const info = aspectRatioInfo.getRatioInfoByKey(selectKey)
      info && addSelectedAspectRatiosState(info)
      event.target.value = ''
    }
  })
})

// 이미 선택된 비율이 있을 경우 return 아닐 경우 추가
function addSelectedAspectRatiosState(info) {
  if (selectedAspectRatiosState.find((item) => item.key === info.key)) {
    return
  }
  setSelectedAspectRatiosState([...selectedAspectRatiosState, info])
}

// 선택된 비율 추가하고, Disable 처리 함수를 호출
function setSelectedAspectRatiosState(newState) {
  selectedAspectRatiosState = newState
  selectedKeyDisabled()
}

// 선택된 비율들 disabled 처리하기
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
    }
  })
}

JSFiddle에서 확인하기

https://jsfiddle.net/sosone/96zvLr4s/