2 คะแนน โดย GN⁺ 2024-10-29 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ฟอร์ม HTML มี ความสามารถตรวจสอบแบบเนทีฟ อยู่แล้ว เช่น required, type="email", pattern, maxlength, setCustomValidity แต่ในการใช้งานจริงกลับถูกใช้ต่ำเมื่อเทียบกับขอบเขตความสามารถ
  • setCustomValidity ซึ่งทรงพลังที่สุด สามารถจัดการลอจิกการตรวจสอบแบบกำหนดเองและกรณีซับซ้อนได้ แต่ถูก提供ให้ใช้ได้เฉพาะในรูปแบบ เมธอดของ DOM จึงไม่เข้ากับคอมโพเนนต์แบบ declarative มากนัก
  • ในสภาพแวดล้อมอย่าง React หากต้องการให้การตรวจสอบค่าเริ่มต้นถูกต้อง จะต้องพัวพันกับ useRef, useLayoutEffect, onChange และลอจิกการตรวจสอบเดียวกันมักถูก ทำซ้ำ ทั้งในการเรนเดอร์ครั้งแรกและในตัวจัดการการเปลี่ยนแปลง
  • หากมี abstraction แบบแอตทริบิวต์อย่าง custom-validity ก็จะสามารถแสดงการตรวจสอบที่มี dependency ระหว่าง state กับ input เช่น การตรวจสอบแบบ asynchronous ว่าชื่อผู้ใช้ซ้ำหรือไม่ หรือการยืนยันรหัสผ่าน ได้ในที่เดียว
  • การที่การตรวจสอบฟอร์มแบบเนทีฟถูกนำไปใช้น้อยนั้นใกล้เคียงกับปัญหาเรื่อง ความใช้งานง่ายของ API มากกว่าการขาดฟีเจอร์ และหากมี API แบบ declarative อยู่ในสเปก HTML ก็จะเพิ่มโอกาสในการใช้งานได้มากขึ้น

สิ่งที่การตรวจสอบฟอร์ม HTML มีให้อยู่แล้ว

  • วิธีที่ง่ายที่สุดในการกันไม่ให้กรอกค่าว่างคือการเพิ่มแอตทริบิวต์ required
  • ข้อจำกัดของ input สามารถกำหนดได้หลัก ๆ สามวิธี
    • ใช้ ประเภท input เช่น type="email", type="number", type="url"
    • ใช้ แอตทริบิวต์ข้อจำกัด เช่น pattern, maxlength
    • ใช้เมธอด DOM setCustomValidity ของ input
  • setCustomValidity เป็นวิธีที่ทรงพลังที่สุด เพราะสามารถจัดการลอจิกการตรวจสอบแบบกำหนดเองและกรณีซับซ้อนได้
  • สองวิธีแรกสามารถประกาศเป็นแอตทริบิวต์ HTML ได้ แต่ setCustomValidity ต่างออกไปตรงที่ต้อง เรียกเมธอด
  • มีการเชื่อมโยงเอกสารอ้างอิง Attributes vs Properties ที่พูดถึงความแตกต่างระหว่างแอตทริบิวต์กับพร็อพเพอร์ตีของ DOM

จุดที่ setCustomValidity เริ่มใช้งานไม่สะดวก

  • setCustomValidity ถูกเปิดเผยให้ใช้เฉพาะในรูปแบบเมธอด โดยไม่มีแอตทริบิวต์ HTML ที่สอดคล้องกัน
  • เมื่อส่งสตริงเข้าไป input จะอยู่ในสถานะ invalid และเบราว์เซอร์จะแสดงสตริงนั้นเป็นเหตุผลที่การตรวจสอบล้มเหลว
  • หากส่งสตริงว่างเข้าไป input จะอยู่ในสถานะ valid ตราบใดที่ไม่มีข้อจำกัดอื่น
  • หากต้องการทำพฤติกรรมแบบเดียวกับ required เอง จะต้องเรียก setCustomValidity ทุกครั้งที่ค่ามีการเปลี่ยนแปลง
  • อย่างไรก็ตาม input จะอยู่ในสถานะ valid ในตอนแรก ดังนั้นหากกดปุ่ม submit ทันทีหลังรีเซ็ตคอมโพเนนต์ การส่งฟอร์มอาจผ่านไปได้
    • เพราะแม้ค่าจะว่าง แต่โค้ดตรวจสอบจะยังไม่ทำงานก่อนเกิด event การเปลี่ยนแปลง
    • หากต้องการให้ครอบคลุมถึงค่าเริ่มต้นด้วย ก็ต้องรันการตรวจสอบเดียวกันตอนที่คอมโพเนนต์ mount ด้วย

Boilerplate ที่เพิ่มขึ้นในคอมโพเนนต์แบบ declarative

  • เมื่อต้องจัดการการตรวจสอบค่าเริ่มต้นด้วย โค้ดจะกลายเป็นงานซ้ำ ๆ และไม่สะดวกอย่างรวดเร็ว
  • โดยเฉพาะจะเกิดปัญหาสามอย่าง
    • ลอจิกการตรวจสอบถูก ทำซ้ำ ทั้งใน handler onChange และขั้นตอนการเรนเดอร์ครั้งแรก
    • โค้ดตรวจสอบเริ่มต้นอยู่ห่างจาก element input ทำให้ cohesion ต่ำลง และมีความเสี่ยงที่จะแก้ไขเพียงด้านใดด้านหนึ่ง
    • การผสม useRef, useLayoutEffect, onChange จะดูเกินจำเป็นเมื่อจำนวน input เพิ่มขึ้น และจะยิ่งสับสนเมื่อมีเพียงบาง input ที่ใช้ customValidity
  • ความซับซ้อนลักษณะนี้จะเด่นชัดเมื่อจัดการ API แบบ imperative ล้วน ๆ ในคอมโพเนนต์แบบ declarative
  • CustomValidity ไม่มีแอตทริบิวต์ input สำหรับตั้งค่าแบบ declarative เหมือนแอตทริบิวต์ตรวจสอบแบบเนทีฟ
  • หาก API ใช้งานยุ่งยาก แม้ฟีเจอร์จะทรงพลังก็อาจถูกนำไปใช้น้อย และนี่คือสาเหตุหลักของการใช้การตรวจสอบฟอร์มแบบเนทีฟไม่เต็มที่

ชิ้นส่วนที่ขาดหายไป: แอตทริบิวต์ custom-validity

  • รูปแบบที่ต้องการคือ แอตทริบิวต์แบบ declarative อย่าง custom-validity ที่กำหนดลงบน input ได้โดยตรง
  • ในเฟรมเวิร์กแบบ declarative แอตทริบิวต์นี้จะช่วยวางสถานะการตรวจสอบไว้ใกล้กับ element input ได้
  • ปัจจุบัน HTML Spec ยังไม่มี custom-validity จริง ๆ
  • สามารถเลียนแบบพฤติกรรมนี้ได้ด้วย implementation ใน userland เพื่อจุดประสงค์ในการสาธิต
  • มี implementation ที่สมบูรณ์กว่า ให้เป็นตัวอย่างคอมโพเนนต์สำหรับ production

การตรวจสอบแบบ asynchronous และ dependency ระหว่าง input

  • การตรวจสอบในแอปพลิเคชันจริงอาจซับซ้อนกว่าการเช็กในเครื่อง
  • input ชื่อผู้ใช้ต้องตรวจสอบกับเซิร์ฟเวอร์ว่า เป็นชื่อที่ถูกใช้อยู่แล้วหรือไม่ และระหว่างที่ request กำลังทำงาน ฟอร์มไม่ควรอยู่ในสถานะ valid
  • ตัวอย่างใช้ required เพื่อกัน input ว่าง และทำให้ input อยู่ในสถานะ invalid ด้วย customValidity ตามสถานะโหลดและผลลัพธ์การตอบกลับ
  • implementation ประกอบด้วยสองส่วน
    • จัดการสถานะ request เพื่อตรวจสอบความ unique ของชื่อผู้ใช้ด้วย useQuery ของ react-query
    • ใช้คอมโพเนนต์ <Input /> แบบกำหนดเองที่รับ prop customValidity ได้
  • วิธีนี้สามารถแสดง flow การตรวจสอบแบบ asynchronous ทั้งหมด รวมถึงสถานะโหลด ข้อผิดพลาด และสำเร็จ ได้ในแอตทริบิวต์เดียว
  • ตัวอย่างเพิ่มเติมเป็นฟอร์มที่บังคับให้กรอกรหัสผ่านซ้ำอีกครั้ง ซึ่งจัดการการตรวจสอบ field input ที่พึ่งพากัน

สรุป

  • setCustomValidity เป็นเครื่องมือที่ทรงพลังซึ่งรองรับความต้องการด้านการตรวจสอบได้หลากหลาย
  • สิ่งที่กำหนดการใช้งานจริงไม่ใช่แค่การมีอยู่ของฟีเจอร์ แต่รวมถึง API ที่ทำให้ฟีเจอร์นั้นใช้งานง่าย
  • abstraction แบบ declarative อย่าง custom-validity ช่วยให้ใช้การตรวจสอบฟอร์มแบบเนทีฟได้อย่างเป็นธรรมชาติมากขึ้น
  • คาดหวังได้ว่าวันหนึ่งฟีเจอร์ลักษณะนี้อาจถูกบรรจุเข้าไปในสเปก HTML แบบเนทีฟ

1 ความคิดเห็น

 
GN⁺ 2024-10-29
ความคิดเห็นบน Hacker News
  • เท่าที่ตรวจดูครั้งล่าสุด เว็บเบราว์เซอร์ยุคนี้ก็ยังไม่สามารถปรับสไตล์หน้าตาของ ข้อความตรวจสอบความถูกต้อง HTML แบบในตัว ได้ https://stackoverflow.com/questions/5328883/how-do-i-style-t...
    ถ้า Chrome กับ Firefox อย่างน้อยทำตามแนวทาง UI ของแพลตฟอร์ม OS จนดูเหมือนเป็นสิ่งที่ระบบแสดงขึ้นมาเอง แบบเดียวกับ tooltip ของ title="" ก็คงยังพอรับได้ แต่ Chrome ใช้ไอคอนสีเหลือง/ส้ม ตัวอักษรสีดำบนพื้นหลังขาว และกล่องคำพูดมุมมนแบบตายตัว ซึ่งชนกับสุนทรียะของโปรเจกต์ปัจจุบันอย่างแรง
    Chrome สมัยก่อนเคยอนุญาตให้ปรับสไตล์ข้อความตรวจสอบความถูกต้องด้วย pseudo-element selector ที่มี vendor prefix แต่หลังจากถอดฟีเจอร์นั้นออกไปแล้วก็ไม่ได้นำกลับมาอีก เลยต้องใส่ไว้ในรายการความหงุดหงิดจิปาถะ เช่น “ขอ HTML combobox แบบเนทีฟหน่อย”, “ทำไมยังเป็นกล่อง Ctrl+คลิกที่ใช้งานยาก แทนที่จะเป็นรายการ checkbox”

    • ปัญหาตรงนี้ไม่ใช่ การไม่มี custom styling ในตัวเองนัก แต่เป็นเพราะการอ่านสถานะ validity แบบเนทีฟของอินพุตแล้วเรนเดอร์ตามต้องการนั้นทำได้ค่อนข้างง่าย
      ปัญหาจริงคือการ subscribe ให้ตรงกับการเปลี่ยนแปลงสถานะความถูกต้องนี้ทำได้ยุ่งยาก มี event ด้านความถูกต้องอยู่ก็จริง แต่ไม่ได้เกิดขึ้นเสมอไป ถ้าเปลี่ยนสถานะฟอร์มด้วยโปรแกรม เช่น เรียก form.reset() หรือ input.value = '...' event เหล่านี้จะไม่ถูก trigger
      ผมมองว่านี่เป็นหัวข้อที่ดีสำหรับไปสืบค้นเพิ่มเติมและเสนอเข้าสู่เว็บแพลตฟอร์ม
    • ไม่ค่อยเข้าใจว่าทำไมถึงอยากปรับสไตล์เรื่องพวกนี้ด้วย การสื่อด้วยสีหรือเลย์เอาต์ก็โอเค แต่เมื่อถึงจุดหนึ่ง ความใช้งานง่าย ก็สำคัญกว่า แบรนดิง
    • ใช่ แต่ก็สามารถซ่อนข้อความเริ่มต้นแล้วแทนที่ด้วยข้อความที่ทำเองได้อยู่ดี ยังได้ประโยชน์ของการตรวจสอบฟอร์มเหมือนเดิม
    • ในแง่นั้น การปรับสไตล์ select แบบเดี่ยว ก็ยังไม่ได้ง่ายเท่าที่ควรจะเป็น
  • สิ่งที่ใหญ่ที่สุด ทำได้ง่าย แต่กลับถูกใช้น้อย คือการใช้ ค่าแอตทริบิวต์ type ที่เฉพาะเจาะจง อย่าง email, number, url
    บนมือถือสามารถแสดงคีย์บอร์ดที่เหมาะที่สุดได้ ทำให้ประสบการณ์ผู้ใช้ดีขึ้นมาก

    • ผมหลีกเลี่ยง type=number แล้วใช้ type=text inputmode=numeric แทน เพราะจะไม่มีปุ่มลูกศรที่ผู้ใช้ส่วนใหญ่ไม่ต้องใช้ในการกรอกตัวเลข และบน iOS คีย์บอร์ดก็ดีกว่า
    • มีที่จำนวนมากอย่างน่าตกใจที่ฟอร์มล็อกอินแสดงคำว่า “email” ไว้ แต่กลับไม่ได้ตั้ง type ให้ถูกต้อง
    • เห็นกรณีแบบนี้เยอะมากกับอินพุต date ด้วย ไลบรารีฟรอนต์เอนด์แต่ละตัวมีวิดเจ็ตวันที่ของตัวเอง และหลายตัวก็ดูแย่มากบนหน้าจอขนาดเล็ก
      ต้องเพิ่ม JS กับ CSS เพียงเพื่อวิดเจ็ตนั้นตัวเดียว และบางตัวก็พึ่งพา jQuery แน่นอนว่าบางตัวมีตัวเลือกตั้งค่าเยอะมาก แต่ด้วยต้นทุนที่เล็กมาก คุณสามารถได้วิดเจ็ตที่ดูใช้ได้ทุกที่และให้ความรู้สึกแบบเนทีฟ โดยทั่วไปก็ตรงกับความต้องการ แถมปล่อยลืมไว้ได้โดยไม่ต้องกังวลเรื่องอัปเดตหรือ CDN
    • น่าเสียดายที่ อินพุต number ยังขาดๆ เกินๆ และไม่สอดคล้องกันระหว่างเบราว์เซอร์ สุดท้ายก็ต้องกลับไปใช้การตรวจสอบด้วย JavaScript อยู่เสมอ
    • ช่วงหลังต้องคอยตรวจให้แน่ใจว่าไม่ได้ใช้องค์ประกอบอินพุตที่ซับซ้อนกว่าเดิม เพราะด้วยเหตุผลหลายอย่าง เราจำเป็นต้องควบคุมการป้อนข้อมูลด้วย คีย์บอร์ดบนหน้าจอ ที่เราทำไว้ในหน้า
      อินพุตทั่วไปยังพอทำได้แบบเฉียดฉิว แต่อินพุตพิเศษรองรับ event น้อยกว่านั้นอีก
  • ผลิตภัณฑ์ของผมไม่ผ่านการตรวจสอบด้านการเข้าถึงได้ เพราะใช้ การตรวจสอบความถูกต้องของฟอร์ม HTML แบบเนทีฟ และคำแนะนำอย่างเป็นทางการคือให้สร้างชั้นการตรวจสอบความถูกต้องเอง ผมก็เห็นด้วยกับคำแนะนำนั้น
    การตรวจสอบความถูกต้องแบบเนทีฟของ HTML มีข้อบกพร่องเยอะ และพูดตรง ๆ การปรับแต่งด้านภาพไม่ใช่เรื่องที่น่ากังวลที่สุดด้วยซ้ำ แต่ก็ค่อนข้างเหมือนตะปูดอกสุดท้ายที่ตอกฝาโลง
    เช่น ถ้าต้องการแสดงข้อผิดพลาดหลายรายการต่อฟิลด์พร้อมกัน ก็ทำได้แค่ต่อสตริงเข้าด้วยกัน เช่น “ต้องมีตัวเลข ต้องมีสัญลักษณ์ ต้องยาวกว่า 10 อักขระ” ซึ่งประสบการณ์ผู้ใช้ก็แย่ และด้านการเข้าถึงได้ก็ไม่ดี เพราะไม่สามารถนำทางสตริงที่ต่อกันใน accessibility tree ได้ นี่ไม่ใช่ปัญหาของการ implement แต่ละเจ้า แต่เป็นปัญหาของตัวสเปกเอง ผู้ใช้คงไม่สนุกกับการเล่นตีตัวตุ่นไล่แก้ข้อผิดพลาดการตรวจสอบความถูกต้อง ดังนั้นควรแสดงข้อผิดพลาดหลายรายการพร้อมกันได้
    การพึ่งพาเบราว์เซอร์ก็แย่เช่นกัน ควบคุมไม่ได้ และ implementation ส่วนใหญ่ก็ไม่ดีนัก Chrome แสดงป๊อปอัปเมื่อโฟกัส ซึ่งเป็นป๊อปอัป ดูเหมือน modal และไม่สามารถแสดงข้อผิดพลาดของฟอร์มทั้งหมดพร้อมกันได้ จึงไม่ดีต่อการเข้าถึงได้ในตัวมันเอง การไม่แสดงข้อมูลสำคัญในป๊อปอัปเป็นพื้นฐานของการเข้าถึงได้ แต่เบราว์เซอร์ก็ทำอะไรได้ไม่มากนักถ้าไม่รบกวนเอกสารจริง
    ข้อผิดพลาดระดับทั้งฟอร์มที่ไม่ได้สังกัดฟิลด์ใดฟิลด์หนึ่งก็ยังต้องใช้การตรวจสอบแบบกำหนดเองอยู่ดี เช่นข้อผิดพลาดอย่าง “ฟิลด์ A และ B ใช้ร่วมกันไม่ได้” และถ้าเป็นแบบนั้น การมีวิธีตรวจสอบความถูกต้องที่สอดคล้องกันย่อมดีกว่า
    หากมีอินพุตแบบกำหนดเองที่ไม่เข้ากับ input type แบบเนทีฟ ก็ต้องมีอินพุตที่ซ่อนไว้เพื่อความสอดคล้อง ซึ่งก็ทำลายการเข้าถึงได้เช่นกัน การแก้เรื่องนี้สุดท้ายก็ยากพอ ๆ กับการสร้างระบบตรวจสอบความถูกต้องของตัวเองที่เป็นมิตรต่อการเข้าถึงได้
    API customValidity เป็นแบบ imperative และใช้งานยุ่งยาก เว้นแต่คุณอยากได้ข้อความแย่ ๆ อย่าง “ฟิลด์นี้ไม่ถูกต้อง” แทบจะไม่มีทางเลือกที่จะไม่ใช้ custom validity ดังนั้นการตรวจสอบความถูกต้องของฟอร์ม HTML จึงแย่มาก

    • เป็นเรื่องย้อนแย้งจริง ๆ ที่ฟีเจอร์ที่เบราว์เซอร์提供ให้อาจไม่ผ่าน ข้อกำหนดด้านการเข้าถึงได้ เพราะเราถูกสอนมาตลอดว่าเหตุผลหนึ่งในการใช้แพลตฟอร์มและทำตามองค์ประกอบเชิงความหมายคือเรื่องการเข้าถึงได้
      ถึงอย่างนั้น ผมก็คิดว่ากลไกการตรวจสอบแบบเนทีฟยังมีคุณค่าให้ใช้อยู่ ไม่จำเป็นต้องใช้ native validity tooltip สำหรับข้อความข้อผิดพลาดเสมอไป สามารถอ่าน ValidityState ของ input.validity โดยตรง แล้วเรนเดอร์ข้อความในแบบที่ต้องการ และแสดงข้อผิดพลาดหลายรายการได้ถ้าจำเป็น
      เบราว์เซอร์สามารถปรับปรุงได้ และถ้าใช้แนวทางที่เป็นมาตรฐาน ก็จะได้รับประโยชน์จากการปรับปรุงนั้น ข้อสรุปทำนองว่า “ถ้าไม่ใช้ป๊อปอัป การเข้าถึงได้จะพัง” ฟังดูแปลก แต่ถ้าต้องรับมือกับการตรวจสอบ ก็อาจต้องไปทางนั้น
      ข้อผิดพลาดที่ไม่ได้สังกัดฟิลด์ใดฟิลด์หนึ่งก็มีเทคนิคจัดการได้ ในโปรเจกต์ของผม ใช้คอมโพเนนต์ HiddenValidationInput ที่สร้างอินพุตอ่านอย่างเดียวที่มองไม่เห็น เพื่อเรนเดอร์ข้อผิดพลาดแบบกำหนดเองที่ไม่ได้สังกัดฟิลด์อื่น แค่เรนเดอร์แบบมีเงื่อนไขก็พอ จึงค่อนข้างใช้งานดีจริง ๆ
      ผมเห็นด้วยเต็มที่ว่า API customValidity เป็นแบบ imperative และยุ่งยาก ผมเขียนบทความที่พูดถึงประเด็นนี้โดยตรงไว้แล้ว และหวังว่าส่วนนี้จะดีขึ้นเมื่อเวลาผ่านไป
    • น่าสนใจ แต่ในแอปของผม ผม implement การตรวจสอบแบบนั้นทั้งหมดแล้ว การตรวจสอบระดับทั้งฟอร์ม จัดการบนเซิร์ฟเวอร์ โดยพื้นฐานคือมีหลายชั้นของการตรวจสอบ และใช้ทุกอย่างที่ใช้ได้
      ดีไซน์ที่หยาบและการเข้าถึงได้ต่ำของ Chrome เป็นปัญหาของ Chrome ดังนั้นผมจะส่ง bug report
      ที่ว่า “สเปก” หมายถึงอันนี้หรือเปล่า? https://html.spec.whatwg.org/multipage/dom.html#concept-elem...
      ผมไม่เห็นส่วนที่นิยามว่าควรแสดงข้อความตรวจสอบความถูกต้องอย่างไร แม้แต่ข้อความที่ว่าต้องมีข้อความก็ยังไม่มี
    • อยากรู้ว่าผู้ตรวจสอบตัดสินแบบนั้นเพราะอะไร
  • พูดตรง ๆ ผมรู้สึกว่าคนที่เขียนสเปกแบบนี้อยู่ห่างไกลจากความเป็นจริง และไม่ได้ใช้สิ่งที่ตัวเองสเปกไว้จริง ๆ มันทำงานได้กับสิ่งที่เรียบง่ายมาก ๆ แต่พอฟอร์มเริ่มซับซ้อนขึ้น สุดท้ายก็จะรู้ว่าการเขียนเองทั้งหมดดีกว่า

    • ใช่ แค่กรณีที่ค่อนข้างพบบ่อยอย่างการ อ้างอิงข้ามกัน กับฟิลด์อื่น ก็แทบจะต้องใช้ JS ทันที เช่น ถ้าผู้ใช้ระบุประเทศแล้ว ก็สามารถตรวจสอบรหัสไปรษณีย์ที่เพิ่งกรอกได้ แต่ถ้ายังไม่ระบุ ก็ต้องรอให้เลือกประเทศก่อน
      ทันทีที่เริ่มใช้ JS การทำทุกอย่างด้วยโค้ดจะง่ายกว่าการไปปรับแอตทริบิวต์ตรวจสอบความถูกต้องมาก
    • ใช่ มันยอดเยี่ยมจนถึงช่วงที่คุณต้องการ date picker แบบข้ามเบราว์เซอร์ แต่หลังจากจุดนั้นก็ต้อง implement เองหลายอย่าง น่าหงุดหงิดที่ผ่านมานานขนาดนี้แล้ว HTML form ยังดิบขนาดนี้อยู่
    • ฟีเจอร์ส่วนใหญ่ของฟอร์มเป็นของยุคต้นทศวรรษ 90 อาจไม่ได้อยู่ในสหัสวรรษเดียวกับผู้เขียนสเปกบางคนด้วยซ้ำ
  • ถ้า checkbox มี label ก็อยากให้ใส่แอตทริบิวต์ for ให้ label เพื่อให้คลิก label แล้วเปิดปิด checkbox ได้ นี่เป็นหนึ่งในเรื่องที่ผมรำคาญที่สุดเป็นการส่วนตัว แต่อาจมีแค่ผมก็ได้

    • ห่อ input ด้วย label ก็ทำงานได้เหมือนกัน ผมไม่ค่อยเข้าใจว่าทำไมคนถึงมักแยกสองอย่างนี้ออกจากกัน
      และก็ไม่เข้าใจว่าทำไมเบราว์เซอร์ถึงเริ่มแยกสิ่งเหล่านี้ออกจากกันด้วย ผมคิดว่าไม่ใช่ให้ข้อความมี checkbox และ radio อยู่ข้างใน แต่ควรเป็น checkbox และ radio ที่มีข้อความอยู่ข้างในมากกว่า
    • ไม่ใช่แค่คุณหรอก ใน ไซต์ที่เข้าถึงได้ ซึ่งทำตาม ADA/WCAG นี่เป็นฟีเจอร์ที่จำเป็น
  • ตัวอย่างง่าย ๆ ที่ไม่ใช้ React อยู่ที่นี่
    https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectE...

    • ผมคิดว่าแปลกมากที่พูดถึงการตรวจสอบความถูกต้องมาตรฐานของ HTML แต่ยกตัวอย่างทั้งหมดด้วย React ถ้าจะสอนว่ามาตรฐานทำงานอย่างไร ก็ไม่ควรถือว่า React เป็นค่าเริ่มต้น
    • ตอนนี้น่าจะทำส่วนใหญ่ได้ด้วย CSS อย่างเดียว แล้ว อาจต้องใช้ JavaScript แค่เพื่อกันไม่ให้ป๊อปอัปพื้นฐานปรากฏขึ้น
  • การตรวจสอบฟอร์มของ HTML นั้นยอดเยี่ยม แต่มีหลุมพรางใหญ่มากอยู่อย่างหนึ่ง
    มันไม่ทำงานใน Firefox สำหรับ Android
    https://bugzilla.mozilla.org/show_bug.cgi?id=1510450

    • ผมใช้ Firefox บนอุปกรณ์มือถือทุกเครื่อง แต่ในประเด็นนี้คงโทษนักพัฒนาที่มองข้าม Firefox สำหรับ Android ได้ยาก เพราะเป็น API ที่เบราว์เซอร์อื่นทั้งหมดทำงานได้มาตั้งแต่ 10 ปีก่อน และความรับผิดชอบในการจัดการความสับสนนี้อยู่ที่ทีม Firefox
      มันไม่คุ้มที่จะเพิกเฉยมาตรฐานเพื่อผู้ใช้น้อยกว่า 1% ที่มองไม่เห็นข้อความผิดพลาดของคอนโทรลที่ถูกทำเครื่องหมายสีแดง ยิ่งเว็บไซต์ใช้สิ่งนี้มากเท่าไร แรงกดดันให้ Mozilla แก้เบราว์เซอร์ก็จะยิ่งมากขึ้นเท่านั้น
      นี่ไม่ใช่ API แบบ WebMIDI ที่ Chrome หรือ Safari นำไป implement ก่อนการทำให้เป็นมาตรฐาน แต่เป็นส่วนหนึ่งของสเปก HTML5 เดิมอยู่แล้ว
    • Firefox สำหรับ Android มีฐานผู้ใช้น้อยกว่า Samsung Internet หรือ Opera และอยู่ราว 0.5% การเสียเวลาไปกับการรองรับมันแทบจะเป็นการสิ้นเปลือง
      ยิ่งเมื่อคิดว่าเราใช้เวลาน้อยแค่ไหนในการทำให้คนที่ใช้ซอฟต์แวร์ช่วยการเข้าถึงสามารถใช้ไซต์ได้อย่างถูกต้องก็ยิ่งเป็นเช่นนั้น ถ้ายังไม่พร้อมจะพูดถึง UC Browser ผมว่าประเด็นแบบนี้ก็ไม่ค่อยมีคุณค่าพอให้หยิบมาพูดถึง
    • ในฐานะคนที่ใช้ Firefox บน Android ทุกวัน สิ่งที่เจ็บปวดที่สุดคือ การตามมาตรฐานไม่ทัน โดยเฉพาะปัญหาเกี่ยวกับ WebGL อย่าง [1] และความไม่สะดวกเล็ก ๆ อย่าง [2]
      ถึงอย่างนั้น การใช้ uBlock Origin ร่วมกับส่วนขยายอื่น ๆ ได้ก็ยังเป็นข้อได้เปรียบที่แข็งแกร่ง
      [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1884282
      [2] https://bugzilla.mozilla.org/show_bug.cgi?id=1897707
    • มันแย่กว่าคำว่า “ไม่ทำงาน” มาก การตรวจสอบทำงาน แต่ ไม่แสดงข้อผิดพลาดเลย
      ถ้าไม่ได้ implement การตรวจสอบเลยยังจะดีกว่า แต่นี่มันเละเทะจริง ๆ ผมเพิ่งรู้หลังจาก debug อย่างยาวนานและทรมานเมื่อหลายปีก่อน และมั่นใจว่าผมคงไม่ใช่คนแรกและไม่ใช่คนสุดท้ายที่เจอเรื่องแบบเดียวกัน
    • เพิ่งตระหนักเรื่องนี้หลังจากเพิ่งเปลี่ยนมาใช้ Firefox บน Android เมื่อไม่นานมานี้ ระหว่างทำแอปอยู่ พยายาม submit ฟิลด์บังคับที่ว่างเปล่า แต่ก็ไม่มีอะไรแสดงขึ้นมาเลยจริง ๆ
      ก่อนจะลองค้นหา ผมนึกไม่ออกเลยว่าตัวเองพลาดอะไรไป และค่อนข้างช็อก การทำการตรวจสอบเองนั้นพอรับได้ แต่สิ่งนี้ควรต้องทำงาน
  • ต้องระวังไม่ใช้มากเกินไป
    เมื่อไม่นานมานี้ผมพยายามขอ refund จาก Groupon เพราะร้านที่ผมซื้อ Groupon ไว้เปลี่ยนผู้บริหารชุดใหม่และไม่ยอมรับ Groupon ของผม
    ฟอร์มมีเงื่อนไขว่า “อย่างน้อย 15 คำ” แต่ไม่ว่าจะทำอย่างไรก็ไม่ผ่านการตรวจสอบ สุดท้ายจึงต้อง inspect HTML
    \w คืออักขระของคำ, \b คือขอบเขตคำ, \s คือช่องว่าง และไม่อนุญาตเครื่องหมายวรรคตอนเลยแม้แต่นิดเดียว

    • ผมว่าเรื่องนี้ไม่ได้เป็นปัญหาเฉพาะของการตรวจสอบด้วย HTML เท่านั้น เรื่องแบบเดียวกันเกิดขึ้นได้กับการตรวจสอบทุกชนิด กฎเดียวกันอาจถูกใช้บนเซิร์ฟเวอร์ด้วยก็ได้ และถ้าเป็นอย่างนั้นก็คงหมดหวัง
    • นี่ก็เป็นหลักฐานที่ดีสำหรับประเด็นที่ผมอยากจะพูดด้วย attribute ตรวจสอบความถูกต้องที่มีอยู่แล้วอย่าง pattern นั้นเจ๋ง แต่ยังไม่พอ
      ตัวอย่างเช่นกรณี “กรอกรหัสผ่านซ้ำ” สามารถ implement ด้วย attribute pattern ได้โดยไม่ต้องใช้ setCustomValidity แต่ต้องสร้าง regex แบบ dynamic จากค่าที่ป้อนครั้งแรก
      ผมไม่ได้เปรียบเทียบวิธีแก้ต่าง ๆ เพราะกลัวบทความจะยาวเกินไป แต่ประเด็นหลักคือเมื่อใช้ customValidity การตรวจสอบจะชัดเจนและอ่านง่ายขึ้นมาก ในกรณีนี้ API ที่ดีกว่าสร้างความแตกต่างได้มาก
      เงื่อนไข “อย่างน้อย 15 คำ” ก็สามารถแสดงให้อ่านง่ายกว่าได้มาก เช่น value.split(/\s+/).length >= 15
  • มีเหตุผลที่มันถูกใช้น้อย เฟรมเวิร์กและไลบรารีจำนวนมากมีฟังก์ชันการตรวจสอบที่แข็งแรงและปรับสไตล์ได้ บางตัวก็ซับซ้อนและขยายได้มาก ถ้าไม่จำเป็นต้องลำบาก ก็ไม่ควรทรมานตัวเอง

    • อย่างไรก็ต้อง implement การตรวจสอบฝั่งแบ็กเอนด์ เองอยู่ดี มักจะมีคนที่พยายามยุ่งกับฟอร์มผ่านเบราว์เซอร์แปลก ๆ, curl, หรือเครื่องมืออื่นที่ไม่มีการตรวจสอบฟอร์มแบบเดียวกันในตัวเสมอ
      เราเชื่อถือไม่ได้ว่าไคลเอนต์ได้ตรวจสอบแล้วหรือทำอย่างถูกต้องหรือไม่ ดังนั้นแบ็กเอนด์ยังต้องตรวจสอบอินพุตและต้องสามารถแสดงฟอร์มพร้อมข้อผิดพลาดการตรวจสอบของทุกฟิลด์ได้
      การตรวจสอบฝั่งฟรอนต์เอนด์มีไว้เพื่อช่วยผู้ใช้เท่านั้น แต่ถ้าจะปรับสไตล์หรือ trigger จากแบ็กเอนด์ตอน submit สุดท้ายก็ต้อง implement สไตล์ของตัวเองอยู่ดี
    • เห็นด้วย ผมพยายามใช้ความสามารถมาตรฐานของเบราว์เซอร์เมื่อทำได้ แต่ การตรวจสอบในตัว ต่อให้ลองพิจารณาแล้วก็ไม่เคยรู้สึกว่าคุ้มค่า
      หลุมพรางมีมากเกินไป และถ้าต้องการรองรับการตรวจสอบที่ซับซ้อนขึ้นได้ง่าย สุดท้ายก็ต้องใช้ไลบรารีอยู่ดี
      นอกจากนี้ การใช้ไลบรารียังอาจเปิดโอกาสให้แชร์โค้ดการตรวจสอบบางส่วนระหว่างฟรอนต์เอนด์กับแบ็กเอนด์ได้ในบางกรณี
      โดยเฉพาะบทความนี้ดูเหมือนจะใช้ useLayoutEffect เพื่อเลี่ยงปัญหาหนึ่ง ซึ่งไม่ใช่สิ่งที่ควรทำแบบเบา ๆ
  • สิ่งหนึ่งที่ผมไม่ชอบในการตรวจสอบฟอร์มของ HTML คือมันเริ่มทำงาน ตั้งแต่ตอนโหลดหน้า ตัวอย่างเช่น ถ้าผูกสไตล์สถานะผิดพลาดไว้กับสิ่งนี้ ฟอร์มจะโหลดขึ้นมาพร้อมข้อผิดพลาดเต็มไปหมดตั้งแต่แรก และอาจดูน่ากดดันสำหรับผู้ใช้

    • มี pseudo-class :user-invalid ที่ช่วยหลีกเลี่ยงเรื่องนี้ได้ในระดับหนึ่ง แต่ก็มีส่วนที่ไม่ยืดหยุ่น จึงอาจไม่พอขึ้นอยู่กับ use case
      https://developer.mozilla.org/en-US/docs/Web/CSS/:user-inval...
    • นี่เป็นหนึ่งในเหตุผลหลักที่ผู้คนเริ่มใช้ JavaScript เพื่อให้แสดงข้อผิดพลาดเฉพาะหลังจากฟอร์มถูก “แตะต้อง” แล้ว หรือก่อน submit เท่านั้น
    • ผมไม่เคยเข้าใจเลยว่าทำไมประสบการณ์แบบนี้ถึงถูกเลือกเป็นค่าเริ่มต้น ผู้ใช้ส่วนใหญ่ไม่ชอบให้ทุกองค์ประกอบของฟอร์มตะโกนใส่ ทั้งที่ยังไม่มีโอกาสได้ทำอะไรเลยด้วยซ้ำ
      เราสามารถใช้สคริปต์อ้อมได้ แต่เมื่อถึงจุดนั้น ฟีเจอร์นี้ก็แทบไม่ได้ช่วยอะไรแล้ว ผมว่านี่คือเหตุผลใหญ่ที่สุดที่มันไม่ถูกนำไปใช้อย่างแพร่หลายกว่านี้