더 강력한 양식 제어 기능

새로운 이벤트와 맞춤 요소 API를 통해 양식에 참여하는 것이 훨씬 더 쉬워졌습니다.

Arthur Evans

많은 개발자는 브라우저에 내장되어 있지 않은 컨트롤을 제공하거나 기본 제공 양식 컨트롤로 가능한 것 이상으로 디자인과 분위기를 맞춤설정하기 위해 맞춤 양식 컨트롤을 빌드합니다.

하지만 기본 제공되는 HTML 양식 컨트롤의 기능을 복제하기는 어려울 수 있습니다. 양식에 <input> 요소를 추가하면 다음과 같이 자동으로 생성되는 몇 가지 기능을 살펴보겠습니다.

  • 입력한 내용은 양식의 컨트롤 목록에 자동으로 추가됩니다.
  • 입력 값은 양식과 함께 자동으로 제출됩니다.
  • 입력이 양식 유효성 검사에 참여합니다. :valid:invalid 의사 클래스를 사용하여 입력의 스타일을 지정할 수 있습니다.
  • 양식이 재설정되거나, 양식이 새로고침되거나, 브라우저에서 양식 항목을 자동 완성하려고 하면 입력 내용에 알림이 전송됩니다.

일반적으로 맞춤 양식 컨트롤에는 이러한 기능이 거의 없습니다. 개발자는 숨겨진 <input>를 양식에 추가하여 양식 제출에 참여하는 등 JavaScript의 일부 제한사항을 해결할 수 있습니다. 그러나 다른 기능은 JavaScript에서만 복제할 수 없습니다.

두 가지 새로운 웹 기능을 사용하면 더 쉽게 맞춤 양식 컨트롤을 만들고 현재 맞춤 컨트롤의 제한사항을 제거할 수 있습니다.

  • formdata 이벤트를 사용하면 임의의 JavaScript 객체가 양식 제출에 참여할 수 있으므로 숨겨진 <input>를 사용하지 않고도 양식 데이터를 추가할 수 있습니다.
  • 양식 관련 맞춤 요소 API를 사용하면 맞춤 요소가 기본 제공 양식 컨트롤처럼 작동하도록 할 수 있습니다.

이 두 기능을 사용하면 더욱 효과적인 새로운 종류의 컨트롤을 만들 수 있습니다.

이벤트 기반 API

formdata 이벤트는 모든 JavaScript 코드가 양식 제출에 참여할 수 있도록 하는 하위 수준 API입니다. 이 메커니즘은 다음과 같이 작동합니다.

  1. 상호작용하려는 양식에 formdata 이벤트 리스너를 추가합니다.
  2. 사용자가 제출 버튼을 클릭하면 양식에서 formdata 이벤트를 실행하며, 이 이벤트는 제출되는 모든 데이터를 보유하는 FormData 객체를 포함합니다.
  3. formdata 리스너는 양식이 제출되기 전에 데이터를 추가하거나 수정할 수 있습니다.

다음은 formdata 이벤트 리스너에서 단일 값을 전송하는 예입니다.

const form = document.querySelector('form');
// FormData event is sent on <form> submission, before transmission.
// The event has a formData property
form.addEventListener('formdata', ({formData}) => {
  // https://developer.mozilla.org/docs/Web/API/FormData
  formData.append('my-input', myInputValue);
});

Glitch에 대한 예를 사용하여 이를 시도해 보세요. API를 실제로 확인하려면 Chrome 77 이상에서 실행해야 합니다.

브라우저 호환성

브라우저 지원

  • 5
  • 12
  • 4
  • 5

소스

양식 연결된 맞춤 요소

이벤트 기반 API는 모든 종류의 구성요소와 함께 사용할 수 있지만 제출 프로세스와의 상호작용만 가능합니다.

표준화된 양식 컨트롤은 제출 외에도 양식 수명 주기의 많은 부분에 관여합니다. 양식과 연결된 맞춤 요소는 맞춤 위젯과 기본 제공 컨트롤 간의 격차를 해소하는 것을 목표로 합니다. 양식 연결된 맞춤 요소는 표준화된 양식 요소의 많은 기능과 일치합니다.

  • <form> 내에 양식 연결된 맞춤 요소를 배치하면 브라우저에서 제공하는 컨트롤처럼 양식과 자동으로 연결됩니다.
  • 이 요소에는 <label> 요소를 사용하여 라벨을 지정할 수 있습니다.
  • 이 요소는 양식과 함께 자동으로 제출되는 값을 설정할 수 있습니다.
  • 요소는 유효한 입력이 있는지 여부를 나타내는 플래그를 설정할 수 있습니다. 양식 컨트롤 중 하나에 잘못된 입력이 있으면 양식을 제출할 수 없습니다.
  • 이 요소는 양식 수명 주기의 다양한 부분(예: 양식이 사용 중지되거나 기본 상태로 재설정되는 경우)에 콜백을 제공할 수 있습니다.
  • 이 요소는 양식 컨트롤(예: :disabled, :invalid)을 위한 표준 CSS 의사 클래스를 지원합니다.

정말 많은 기능이에요! 이 도움말에서는 이러한 방법을 모두 다루지는 않지만 맞춤 요소를 양식에 통합하는 데 필요한 기본사항에 대해 설명합니다.

양식 관련 맞춤 요소 정의

맞춤 요소를 양식과 연결된 맞춤 요소로 바꾸려면 몇 가지 추가 단계를 거쳐야 합니다.

  • 맞춤 요소 클래스에 정적 formAssociated 속성을 추가합니다. 이렇게 하면 브라우저가 요소를 양식 컨트롤처럼 취급합니다.
  • 요소에서 attachInternals() 메서드를 호출하여 setFormValue()setValidity()와 같은 양식 컨트롤의 추가 메서드와 속성에 액세스합니다.
  • 양식 컨트롤에서 지원되는 공통 속성 및 메서드(예: name, value, validity)를 추가합니다.

이러한 항목이 기본 맞춤 요소 정의에 들어가는 방식은 다음과 같습니다.

// Form-associated custom elements must be autonomous custom elements--
// meaning they must extend HTMLElement, not one of its subclasses.
class MyCounter extends HTMLElement {

  // Identify the element as a form-associated custom element
  static formAssociated = true;

  constructor() {
    super();
    // Get access to the internal form control APIs
    this.internals_ = this.attachInternals();
    // internal value for this control
    this.value_ = 0;
  }

  // Form controls usually expose a "value" property
  get value() { return this.value_; }
  set value(v) { this.value_ = v; }

  // The following properties and methods aren't strictly required,
  // but browser-level form controls provide them. Providing them helps
  // ensure consistency with browser-provided controls.
  get form() { return this.internals_.form; }
  get name() { return this.getAttribute('name'); }
  get type() { return this.localName; }
  get validity() {return this.internals_.validity; }
  get validationMessage() {return this.internals_.validationMessage; }
  get willValidate() {return this.internals_.willValidate; }

  checkValidity() { return this.internals_.checkValidity(); }
  reportValidity() {return this.internals_.reportValidity(); }

  …
}
customElements.define('my-counter', MyCounter);

등록이 완료되면 브라우저에서 제공한 양식 컨트롤을 사용할 때마다 이 요소를 사용할 수 있습니다.

<form>
  <label>Number of bunnies: <my-counter></my-counter></label>
  <button type="submit">Submit</button>
</form>

값 설정

attachInternals() 메서드는 양식 제어 API에 대한 액세스 권한을 제공하는 ElementInternals 객체를 반환합니다. 가장 기본적인 방법은 컨트롤의 현재 값을 설정하는 setFormValue() 메서드입니다.

setFormValue() 메서드는 다음 세 가지 유형의 값 중 하나를 사용할 수 있습니다.

  • 문자열 값입니다.
  • File 객체
  • FormData 객체 FormData 객체를 사용하여 여러 값을 전달할 수 있습니다. 예를 들어 신용카드 입력 컨트롤은 카드 번호, 만료일, 인증 코드를 전달할 수 있습니다.

단순 값을 설정하는 방법:

this.internals_.setFormValue(this.value_);

여러 값을 설정하려면 다음과 같이 하면 됩니다.

// Use the control's name as the base name for submitted data
const n = this.getAttribute('name');
const entries = new FormData();
entries.append(n + '-first-name', this.firstName_);
entries.append(n + '-last-name', this.lastName_);
this.internals_.setFormValue(entries);

입력 검증

컨트롤은 내부 객체에서 setValidity() 메서드를 호출하여 양식 유효성 검사에 참여할 수도 있습니다.

// Assume this is called whenever the internal value is updated
onUpdateValue() {
  if (!this.matches(':disabled') && this.hasAttribute('required') &&
      this.value_ < 0) {
    this.internals_.setValidity({customError: true}, 'Value cannot be negative.');
  }
  else {
    this.internals_.setValidity({});
  }
  this.internals.setFormValue(this.value_);
}

기본 제공 양식 컨트롤과 마찬가지로 :valid:invalid 의사 클래스를 사용하여 양식 연결된 커스텀 요소의 스타일을 지정할 수 있습니다.

수명 주기 콜백

양식 연결된 맞춤 요소 API에는 양식 수명 주기에 연결할 수 있는 추가 수명 주기 콜백 집합이 포함되어 있습니다. 콜백은 선택사항입니다. 요소가 수명 주기의 특정 시점에 무언가를 해야 하는 경우에만 콜백을 구현합니다.

void formAssociatedCallback(form)

브라우저가 요소를 양식 요소와 연결하거나, 양식 요소에서 요소의 연결을 해제할 때 호출됩니다.

void formDisabledCallback(disabled)

이 요소의 disabled 속성이 추가 또는 삭제되었거나 이 요소의 상위 항목인 <fieldset>에서 disabled 상태가 변경되어 요소의 disabled 상태가 변경된 후에 호출됩니다. disabled 매개변수는 요소의 새로운 사용 중지 상태를 나타냅니다. 예를 들어, 요소가 사용 중지되면 shadow DOM의 요소를 사용 중지할 수 있습니다.

void formResetCallback()

양식이 재설정된 후 호출됩니다. 요소는 자체적으로 일종의 기본 상태로 재설정되어야 합니다. <input> 요소의 경우 일반적으로 마크업에 설정된 value 속성과 일치하도록 value 속성을 설정합니다. 체크박스의 경우 checked 속성과 일치하도록 checked 속성을 설정해야 합니다.

void formStateRestoreCallback(state, mode)

다음 두 가지 상황 중 하나에서 호출됩니다.

  • 브라우저에서 요소의 상태를 복원하는 경우 (예: 탐색 후 또는 브라우저가 다시 시작된 경우) 이 경우 mode 인수는 "restore"입니다.
  • 양식 자동 완성과 같은 브라우저의 입력 지원 기능이 값을 설정하는 경우. 이 경우 mode 인수는 "autocomplete"입니다.

첫 번째 인수의 유형은 setFormValue() 메서드가 호출된 방법에 따라 다릅니다. 자세한 내용은 양식 상태 복원을 참고하세요.

양식 상태 복원 중

특정 상황(예: 페이지로 다시 돌아가거나 브라우저를 다시 시작할 때)에서는 브라우저에서 사용자가 기존에 있던 상태로 양식을 복원하려고 할 수 있습니다.

양식 연결된 맞춤 요소의 경우 복원된 상태는 setFormValue() 메서드에 전달하는 값에서 가져옵니다. 앞의 예에 표시된 대로 단일 값 매개변수를 사용하거나 다음 두 개의 매개변수를 사용하여 메서드를 호출할 수 있습니다.

this.internals_.setFormValue(value, state);

value는 컨트롤의 제출 가능한 값을 나타냅니다. 선택사항인 state 매개변수는 컨트롤의 내부 표현으로, 서버로 전송되지 않는 데이터가 포함될 수 있습니다. state 매개변수는 value 매개변수와 동일한 유형을 취하며 문자열, File, FormData 객체일 수 있습니다.

state 매개변수는 값만으로는 컨트롤의 상태를 복원할 수 없는 경우에 유용합니다. 예를 들어 팔레트나 RGB 색상환 등 여러 모드로 색상 선택 도구를 만든다고 가정해 보겠습니다. 제출 가능한 value"#7fff00"과 같은 표준 형식으로 선택된 색상입니다. 그러나 컨트롤을 특정 상태로 복원하려면 어떤 모드에 있었는지도 알아야 하므로 state"palette/#7fff00"와 같이 보일 수 있습니다.

this.internals_.setFormValue(this.value_,
    this.mode_ + '/' + this.value_);

코드는 저장된 상태 값에 따라 상태를 복원해야 합니다.

formStateRestoreCallback(state, mode) {
  if (mode == 'restore') {
    // expects a state parameter in the form 'controlMode/value'
    [controlMode, value] = state.split('/');
    this.mode_ = controlMode;
    this.value_ = value;
  }
  // Chrome currently doesn't handle autofill for form-associated
  // custom elements. In the autofill case, you might need to handle
  // a raw value.
}

더 간단한 컨트롤 (예: 숫자 입력)의 경우 이 값은 컨트롤을 이전 상태로 복원하는 데 충분할 수 있습니다. setFormValue()를 호출할 때 state를 생략하면 값이 formStateRestoreCallback()에 전달됩니다.

formStateRestoreCallback(state, mode) {
  // Simple case, restore the saved value
  this.value_ = state;
}

실제 사례

다음 예는 양식과 연결된 맞춤 요소의 여러 기능을 모아 놓은 것입니다. API를 실제로 확인하려면 Chrome 77 이상에서 실행해야 합니다.

특성 감지

특성 감지를 사용하여 formdata 이벤트 및 양식 관련 맞춤 요소를 사용할 수 있는지 확인할 수 있습니다. 현재 두 기능에 대해 출시된 폴리필은 없습니다. 두 경우 모두 숨겨진 양식 요소를 추가하여 컨트롤 값을 양식에 전파할 수 있습니다. 양식과 연결된 맞춤 요소의 고급 기능 중 상당수는 폴리필이 어렵거나 불가능할 가능성이 높습니다.

if ('FormDataEvent' in window) {
  // formdata event is supported
}

if ('ElementInternals' in window &&
    'setFormValue' in window.ElementInternals.prototype) {
  // Form-associated custom elements are supported
}

결론

formdata 이벤트 및 양식 관련 맞춤 요소는 맞춤 양식 컨트롤을 만들 수 있는 새로운 도구를 제공합니다.

formdata 이벤트는 새로운 기능을 제공하지 않지만 숨겨진 <input> 요소를 만들지 않고도 양식 데이터를 제출 프로세스에 추가할 수 있는 인터페이스를 제공합니다.

양식 관련 맞춤 요소 API는 기본 제공 양식 컨트롤처럼 작동하는 맞춤 양식 컨트롤을 만들기 위한 새로운 기능 모음을 제공합니다.

우돔 프라바트가 촬영한 Unsplash의 히어로 이미지