크롬 135버전부터 버튼에 command 및 commandfor 도입
(developer.chrome.com)- 버튼은 동적 웹 애플리케이션을 만드는 데 필수임. 메뉴를 열고, 작업을 전환하고, 폼을 제출하는데 사용
- Chrome 135에서는 새로운
command
및commandfor
속성으로 이전의popovertargetaction
및popovertarget
속성을 개선하고 대체함 - 기존에 버튼 동작을 구현할 때 발생하는 문제점:
-
HTML의
onclick
핸들러는 보안 정책(CSP)으로 인해 실제 코드에서 사용이 제한될 수 있음 - 버튼과 다른 요소의 상태 동기화가 필요하며, 접근성을 유지하면서 상태를 관리하는 코드는 복잡함
- React, AlpineJS, Svelte 등에서도 상태 및 이벤트 핸들링이 복잡함
-
HTML의
command
와 commandfor
패턴
-
command
와commandfor
속성을 사용하면 버튼이 다른 요소에 대해 선언적으로 동작할 수 있음. 이는 프레임워크의 편리함을 제공하면서도 유연성을 유지 -
commandfor
버튼은 ID를 사용(for
속성과 비슷) 하고,command
는 내장 값을 받아 더 직관적인 접근 방식을 제공 - 예제: 메뉴 열기 버튼 구현
- aria-expanded나 추가적인 JavaScript가 필요하지 않음
<button commandfor="my-menu" command="show-popover"> Open Menu </button> <div popover id="my-menu"> <!-- ... --> </div>
command
와 commandfor
vs popovertargetaction
과 popovertarget
-
popover
를 사용한 적이 있다면popovertarget
과popovertargetaction
속성에 익숙할 수 있음 - 이들은
commandfor
와command
와 유사하게 작동하지만, 팝오버에 특화 - 새로운 속성은 이전 속성을 완전히 대체하며, 추가 기능을 제공함
내장 명령
-
command
속성은 다양한 API와 매핑되는 동작들을 내장-
show-popover
:el.showPopover()
와 매핑됨 -
hide-popover
:el.hidePopover()
와 매핑됨 -
toggle-popover
:el.togglePopover()
와 매핑됨 -
show-modal
:dialogEl.showModal()
와 매핑됨 -
close
:dialogEl.close()
와 매핑됨
-
- 예제: 삭제 확인 다이얼로그 구현
- JavaScript 없이 상태 및 접근성 관리 가능
<button commandfor="confirm-dialog" command="show-modal"> Delete Record </button> <dialog id="confirm-dialog"> <header> <h1>Delete Record?</h1> <button commandfor="confirm-dialog" command="close" aria-label="Close"> <img role="none" src="/close-icon.svg"> </button> </header> <p>Are you sure? This action cannot be undone</p> <footer> <button commandfor="confirm-dialog" command="close" value="cancel"> Cancel </button> <button commandfor="confirm-dialog" command="close" value="delete"> Delete </button> </footer> </dialog>
- 결과 처리 코드: 다이얼로그의 close 이벤트에서 반환 값 처리 가능
dialog.addEventListener("close", (event) => { if (event.target.returnValue === "cancel") { console.log("Cancel was clicked"); } else if (event.target.returnValue === "delete") { console.log("Delete was clicked"); } });
사용자 정의 명령
- 내장 명령 외에도
--
접두사를 사용하여 사용자 정의 명령을 정의할 수 있음 - 사용자 정의 명령은 대상 요소에서
"command"
이벤트를 발생시키지만, 추가적인 로직은 수행하지 않음 - 예제: 이미지 회전 명령 구현
<button commandfor="the-image" command="--rotate-landscape"> Landscape </button> <button commandfor="the-image" command="--rotate-portrait"> Portrait </button> <img id="the-image" src="photo.jpg"> <script type="module"> const image = document.getElementById("the-image"); image.addEventListener("command", (event) => { if (event.command === "--rotate-landscape") { image.style.rotate = "-90deg"; } else if (event.command === "--rotate-portrait") { image.style.rotate = "0deg"; } }); </script>
Shadow DOM에서 명령 처리
- Shadow DOM에서는 commandfor가 ID를 기반으로 작동하기 때문에 다음과 같은 제한 사항이 있음:
- Shadow DOM 간에 요소 참조 불가
- 이 경우 JavaScript API를 사용하여
.commandForElement
속성을 설정할 수 있음
- 예제: Shadow DOM에서 명령 연결
<my-element> <template shadowrootmode="open"> <button command="show-popover">Show popover</button> <slot></slot> </template> <div popover><!-- ... --></div> </my-element> <script> customElements.define("my-element", class extends HTMLElement { connectedCallback() { const popover = this.querySelector('[popover]'); this.shadowRoot.querySelector('button').commandForElement = popover; } }); </script>
향후 계획
- Chrome에서는 추가 내장 명령 도입을 계획 중:
- <details> 요소 열기 및 닫기
- <input> 및 <select>에서 "show-picker" 명령 지원
- <video> 및 <audio> 재생 명령
- 요소에서 텍스트 복사 기능
Hacker News 의견
-
프로그래밍 언어 이론가들은 80년대부터 "goto"의 더 강력한 버전인 "comefrom"에 대해 추측해 왔음. 이는 intercal에서만 구현되었음. intercal은 C와 같은 언어보다 안전성, 성능, 인체공학적으로 우수하지만 상업 시장에 진입하는 데 어려움을 겪고 있음. javascript가 intercal의 이 기능을 통합하는 것을 보는 것은 흥미로움. 이는 javascript의 클로저 기반 객체가 함수형 프로그래밍을 주류로 이끈 것처럼 정중한 프로그래밍의 급증으로 이어질 수 있기를 바람
-
Invokers는 Chrome만의 것이 아님. Firefox nightly에서도 이미 사용 가능함
-
JS 없이 선언적 UI 동작을 구현하는 아이디어는 매력적임
- 팝오버/모달의 보일러플레이트를 제거함 (aria-expanded 조작 불필요)
- show-modal과 같은 내장 명령은 접근성을 마크업에 통합함
- 사용자 정의 명령(e.g., --rotate-landscape)은 HTML을 통해 컴포넌트가 API를 노출할 수 있게 함
-
의문점:
- 추상화 vs. 마법: 이는 단순히 복잡성을 JS에서 HTML로 이동시키는 것인가? 프레임워크는 이미 상태를 추상화함. 이는 어떻게 공존할 것인가?
- Shadow DOM 마찰: shadow roots 간에 .commandForElement를 설정하기 위해 JS가 필요함. 이는 절반만 해결된 문제처럼 보임
- 미래 대비: OpenUI가 20개 이상의 명령(e.g., show-picker, toggle-details)을 추가하면, 플랫폼이 틈새 구문으로 부풀어 오를 것인가?
-
사양:
- button element, commandfor 속성
- button element, command 속성
-
이것이 Next, Be, Apple 등이 약 30년 전에 사용한 액션/메시징 패턴인가, 아니면 내가 뭔가 놓친 것인가
- 이는 유용했지만 기본적인 디자인 패턴을 유지하려는 복잡성 때문에 인터페이스 기반 컨트롤러 패턴으로 진화했음. 따라서 이 상자가 열리면 많은 개선 요청이 있을 것으로 예상함
-
Netscape의 초기 Java UI 툴킷(IFC)이 액션 요소를 연결할 수 있게 했음
-
새로운 command 및 commandfor 속성은 popovertargetaction 및 popovertarget 속성을 개선하고 대체함
- 이것들이 기본적으로 사용 가능해진 것인가? 대체한다는 것은 무슨 의미인가? 언젠가 이를 제거할 것인가? 웹 개발자들이 더 이상 필요하지 않은 것을 업데이트로 제거할 수는 없음
-
문자열로 프로그래밍하는 것에 완전히 알레르기 반응을 보임. 접근성 이점은 이해하지만, 또 다른 웹 앱 동작 레이어로 요소 ID를 사용하는 것에 대해 특별히 흥미롭지 않음
-
전체 API 없이 이를 구현하지 말았어야 함. 5개 정도의 명령 대신, HTML을 통해 모든 JavaScript 기능을 구현할 수 있는 것처럼 보임. 이는 수천 개의 명령이 될 수 있음
-
HTML에서 command and conquer에 대한 기대감이 있었음
-
HTML을 개선하고 확장하는 것은 좋지만, 아직 갈 길이 멀음. HTMX 팀이 몇 가지 좋은 아이디어를 가지고 있음