본문 바로가기
빌드노트 검색

빌드노트

input.value를 바꿨는데 reset하면 예전 값으로 돌아간다

텍스트 입력의 value를 바꿔도 reset 기준은 그대로일 수 있다. 현재값·defaultValue·value 속성을 비교하고, 편집 여부에 따른 기본값 반영 차이와 두 값을 함께 갱신할 시점을 설명한다.

텍스트 입력에 input.value = "script"를 대입했는데 폼을 초기화하자 예전 문자열이 다시 나타날 수 있다. value 대입은 현재 입력을 바꾸고, reset은 기본값을 다시 적용한다. 새 값을 다음 reset의 기준으로도 삼으려는 경우에는 defaultValue를 갱신해야 한다.

문제가 나는 입력의 현재값과 기본값부터 함께 읽는다. 개발자 도구 Console에서 아래 #displayName을 자기 입력의 선택자로 바꿔 실행한다. 폼에 속한 네이티브 input type="text"를 대상으로 하며, 선택 결과가 null이면 먼저 선택자를 확인한다.

const input = document.querySelector("#displayName");
const form = input.form;
const read = () => console.log(JSON.stringify([
  input.value, input.defaultValue, input.getAttribute("value")
]));
read();

출력은 [현재값, 기본값, value 속성값] 순서다. 화면에는 script가 보이는데 ["script","seed","seed"]가 나온다면, 화면값만 바뀌고 reset 기준은 seed로 남아 있다. 개발자 도구 Elements에 보이는 value="seed"를 현재 입력이라고 읽으면 이 차이를 놓친다.

MDN의 defaultValue 설명 (새 탭에서 열림)에 따르면 defaultValue는 HTML의 value 속성을 반영한다. 기본값은 최초 HTML 원문을 영구 보관한 값이 아니라, 스크립트로 바꿀 수 있는 DOM의 값이다. MDN의 reset 설명 (새 탭에서 열림)도 기본값 복원을 설명하며, setAttribute()로 바꾼 속성을 원래 값으로 되돌리지 않는다고 명시한다.

사용자 입력과 스크립트 대입을 비교했다

이 글을 위해 Windows 11에서 새 프로필의 headless Chrome for Testing 153.0.8010.36을 실행했다. 새 loopback 폼에 합성 문자열만 넣었고 외부로 제출하지 않았다. 키보드 편집은 브라우저 자동화로 수행했다. 아래는 운영 장애 기록이 아니라 별도 실험의 관찰이다.

작은 폼으로 확인하려면 다음 내용을 UTF-8의 demo.html로 저장해 브라우저에서 연다. 외부 자원이나 서버 없이 실행할 수 있고, 버튼은 폼을 초기화한다. 이 문서의 Console에서 앞의 조회 코드를 실행하면 된다. 새 문서에서는 Console 변수도 다시 선언한다.

<!doctype html>
<meta charset="utf-8">
<title>텍스트 입력 초기화</title>
<form id="profile" autocomplete="off">
  <label>표시 이름 <input id="displayName" type="text" value="seed"></label>
  <button type="reset">초기화</button>
</form>

처음 read()는 ["seed","seed","seed"]였다. 입력 내용을 모두 선택해 typed로 고치자 ["typed","seed","seed"]가 됐다. 초기화 버튼을 누르고 다시 읽으면 세 값 모두 seed였다. 사용자 편집이 현재값만 바꾼 것이다.

다음 코드는 초기화된 같은 폼에서 스크립트 대입이 reset 기준까지 바꾸는지 확인한다.

input.value = "script";
read();       // ["script","seed","seed"]
form.reset();
read();       // ["seed","seed","seed"]

실험에서도 주석과 같은 값이 나왔다. 화면에 다른 레코드를 채우는 코드가 value만 바꾼다면, 취소 버튼은 여전히 이전 기본값으로 돌아갈 수 있다.

defaultValue는 편집 중인 현재값을 덮어쓰지 않았다

초기화 직후 input.defaultValue = "loaded"를 실행하자 현재값·기본값·속성값 모두 loaded가 됐다. 그러나 그 입력을 키보드로 edited로 고친 뒤 input.defaultValue = "saved"를 실행하자 결과가 달랐다.

  • 편집 직후: ["edited","loaded","loaded"]
  • defaultValue 대입 뒤: ["edited","saved","saved"]
  • 이어서 form.reset() 호출 뒤: ["saved","saved","saved"]

편집 중인 현재값은 남았지만 다음 reset의 기준은 바뀌었다. 따라서 defaultValue가 화면을 항상 바꾼다고 가정해도, 화면에는 영향을 주지 않는다고 가정해도 틀린다.

HTML 표준의 dirty value flag 규칙 (새 탭에서 열림)은 이 차이를 설명한다. 사용자 편집이나 value 대입은 내부 표시를 켠다. 표시가 꺼져 있을 때만 value 속성 변경이 현재값에도 반영된다. reset은 표시를 끄고 그때의 value 속성값을 적용하며, 속성이 없으면 빈 문자열을 적용한다. 이 실험은 내부 표시를 직접 읽은 것이 아니라, 값의 변화를 표준 절차와 대조한 것이다.

setAttribute("value", ...)로 바꿔도 편집 후에는 현재값이 남고 reset 때 새 속성값이 적용됐다. 또 input.value = input.value처럼 같은 문자열을 다시 대입한 뒤 defaultValue를 바꾸자 현재값은 그대로였다. 현재값과 기본값이 같다는 사실만으로 편집 전 상태라고 판단할 수는 없다.

화면과 reset 기준을 함께 교체하려면

다른 레코드를 불러와 현재 입력과 취소 기준을 모두 바꾸려면 두 프로퍼티를 명시적으로 설정한다. 아래 new-record는 비교용 문자열이며, 실제 화면에서는 불러온 값으로 바꾼다.

const nextName = "new-record";
input.defaultValue = nextName;
input.value = nextName;

실험에서는 세 값 모두 new-record가 됐다. 이어 현재값을 draft로 바꾸고 reset을 호출하자 new-record로 돌아왔다. 이 두 줄은 해당 필드를 갱신하므로, 기본값을 적용하려고 폼 전체를 reset해 다른 필드의 편집까지 버릴 필요도 없다.

이번 입력만 수정하고 취소하면 이전 값으로 돌아가야 한다면 value만 바꾼다. 저장 성공 후 그 저장값을 새 취소 기준으로 삼으려면, 성공이 확인된 값으로 defaultValue를 갱신한다. 편집할 때마다 기본값까지 덮어쓰면 취소해도 돌아갈 이전 기준이 남지 않는다. 저장을 기다리는 동안 추가 편집을 허용한다면 저장된 값과 현재 입력도 구분해야 한다.

자기 폼의 defaultValue까지 예상과 다르면 이 프로퍼티에 대입하거나 value 속성을 바꾸는 코드를 찾는다. 세 값은 예상대로인데 reset 뒤 결과가 다르면 reset을 취소하는 이벤트 핸들러, 값을 다시 대입하는 코드, 입력 요소를 새로 만드는 코드도 확인한다.

직접 확인한 범위는 Chrome 한 버전의 네이티브 텍스트 입력, 키보드 편집, 프로퍼티·속성 대입과 취소하지 않은 reset이다. value 속성을 제거한 입력은 reset 뒤 빈 문자열이 됐지만, 값이 있는 폼까지 reset이 항상 비우는 것은 아니다. Firefox·Safari·모바일, 프레임워크가 관리하는 입력, reset 이벤트 취소와 실제 서버 저장은 검증하지 않았다. 체크박스의 선택 상태는 checked와 별도 규칙을 쓰므로 이 문자열 실험에 포함하지 않았다.

← 전체 글 보기