문제 상황
<select> 태그 내부의 옵션 중, 현재 상태값과 일치하는 옵션에 selected 속성을 조건부로 삽입했지만, 페이지가 렌더링 된 이후 항상 "total" 옵션이 선택되어 있었다.
예를 들어, 아래와 같은 코드에서 sortBy 값이 "fun"인 경우에도 "total"이 선택된 상태로 나타났다.
console을 찍어보면 sortBy는 "fun"으로 정확하게 전달되고 있었지만, 실제 <select>에서는 "total"이 선택된 상태였다.
this.template = () => {
return `
<select id="sortList">
<option value="total" ${sortBy === "total" ? "selected" : ""}>Total</option>
<option value="cost" ${sortBy === "cost" ? "selected" : ""}>Cost</option>
<option value="fun" ${sortBy === "fun" ? "selected" : ""}>Fun</option>
</select>
`;
};
원인
이 문제의 핵심 원인은 selected 속성이 HTML 파싱 시점에서만 적용된다는 점에 있다.
브라우저는 <select> 요소가 HTML로 파싱될 때, selected 속성이 붙어 있는 <option>을 찾아 초기 선택값으로 설정한다.
하지만 JavaScript에서 innerHTML을 사용하여 <select> 요소를 동적으로 렌더링 하면,
이 HTML은 파싱이 아닌 문자열로 DOM에 삽입된다.
이 경우, selected 속성은 무시되며 <select>의 .value가 명시되지 않은 경우 기본적으로 첫 번째 옵션이 선택된다.
따라서 innerHTML을 사용해 <select>를 다시 렌더링할 경우, selected 속성을 아무리 넣어도 원하는 옵션이 선택되지 않을 수 있다고 한다.
해결책
selected 속성을 문자열로 삽입하는 대신, 렌더링 이후에 JavaScript로 .value를 직접 설정해야 한다.
아래는 문제를 해결한 코드이다.
this.render = () => {
this.$target.innerHTML = this.template();
const $select = document.getElementById("sortList");
$select.value = this.state.sortBy;
};