สอนวิธีเขียน CSS แบบ BEM

bem

เราคงจะเคยเจอปัญหาในการตั้งชื่อ class ให้กับ html element ต่างๆ กันมาบ้าง ทั้งเรื่องการตั้งชื่อ class อย่างไรให้ไล่โค้ดได้ง่าย รวมไปถึงการตั้งชื่อ class อย่างไรที่จะส่งผลดีในแง่ของ performance บทความนี้เราเลยจะมาทำความรู้จักกับวิธีการตั้งชื่อ class ที่กำลังมาแรงในขณะนี้ที่มีชื่อว่า BEM เราลองมาดูกันว่าวิธีนี้มันมีดีอะไร

BEM คืออะไร ?

BEM หรือ “Block Element Modifier” คือวิธีการตั้งชื่อ class อย่างเป็นระบบ เพื่อที่จะทำให้เราสามารถไล่โค้ดได้ง่าย และลดความซ้ำซ้อนของโค้ด โดยการตั้งชื่อ class ด้วยวิธี BEM นั้น จะดูจากหน้าที่ของ html element นั้นๆ ซึ่งแบ่งออกเป็น 3 แบบ ด้วยกัน ดังนี้

  • Blockกล่องอะไรก็ตามที่อยู่ได้โดยอิสระ (เช่น กล่อง search, กล่อง log in, เมนูหลัก เป็นต้น)
  • Elementองค์ประกอบต่างๆ ของ Block (หาก Block เป็นคน Element ก็หมายถึงแขนขาของคนนั่นเอง)
  • Modifierใส่ให้กับ Block หรือ Element ที่มีสไตล์เฉพาะตัว (Modifier ของ Block ก็เช่น คนที่เป็นผู้หญิง ส่วน Modifier ของ Element ก็เช่น แขนข้างซ้าย เป็นต้น)

เมื่อจำแนก html element นั้นๆ ได้แล้ว ว่าเป็นแบบไหน เราก็จะตั้งชื่อ class ตามรูปแบบนี้

Workshop – เขียน CSS แบบ BEM

เพื่อให้เห็นภาพมากขึ้น ลองดูตัวอย่างโค้ด html ต่อไปนี้

เริ่มจากกำหนด Block และ Element

จากโค้ดด้านบน หากเราจะตั้งชื่อ class ด้วยวิธี BEM เราก็จะมอง form ทั้ง 2 เป็น Block และ input ต่างๆ ภายใน form ก็จะเป็น Element ให้เราเพิ่ม class ให้กับ html element ต่างๆ ตามรูปแบบของ BEM ที่ได้กล่าวไปก่อนหน้านี้ เราก็จะได้โค้ด html หน้าตาแบบนี้

ใส่ Modifier หากต้องการกำหนดสไตล์พิเศษ

ส่วน Modifier นั้น เราจะใช้เฉพาะกรณีที่เราต้องการจะใส่สไตล์พิเศษให้กับ Block หรือ Element เท่านั้น ซึ่งในตัวอย่างนี้ เราอาจจะใช้แยกความแตกต่างระหว่างช่อง input กับปุ่ม submit ก็ได้ โค้ด html ก็จะได้แบบนี้

จะเห็นว่าที่ปุ่ม submit ของเราจะมีอยู่ 2 class ด้วยกัน .form__input เป็นการบอกว่า html element นี้ เป็น input ของ form นะ ส่วน .form__input--submit เป็นการบอกว่า input ของ form อันนี้ เป็นแบบ submit นะ

แต่ถ้าเราอยากให้ 2 ฟอร์มนี้ มีหน้าตาต่างกันด้วย ก็ให้เราใช้ Modifier มาช่วยเหมือนเดิม

เช่นเดียวกับตัวอย่างก่อนหน้านี้ ที่แต่ละฟอร์มต่างมีอยู่ 2 class เช่นกัน คือ class กลางๆ ที่บอกว่านี่คือฟอร์มนะ กับอีก class หนึ่ง ที่เอาไว้บอกว่าฟอร์มนี้เป็นฟอร์มอะไร

ส่วนในกรณีที่เราต้องการให้ปุ่ม submit ของทั้ง 2 ฟอร์ม มีหน้าตาต่างกัน เราก็จะเขียนแบบนี้

จะเห็นว่าเราจำเป็นต้องเพิ่ม class ที่ 3 ให้กับปุ่ม submit เพื่อเป็นการบอกว่าปุ่ม submit นี้ เป็นของฟอร์มสำหรับ search นะ ส่วนปุ่ม submit นี้ เป็นของฟอร์มสำหรับ login นะ

อย่างไรก็ตาม การตั้งชื่อ class นั้น พยายามอย่าให้เกิน 3 ระดับ เช่น block__element--modifier หรือ block--modifier__element เพราะถ้ามันยาวกว่านี้เราก็จะเริ่มดูลำบากแล้ว

Block สามารถอยู่ในอีก Block ได้

ภายใน Block สามารถมี Block อื่นอยู่ได้ด้วย ลองดูตัวอย่างต่อไปนี้

จากโค้ดด้านบน เรามอง header เป็น Block ก็จะได้ว่าฟอร์มทั้ง 2 เป็น Element ของ header แต่ในขณะเดียวกัน เราก็มองว่าฟอร์มทั้ง 2 เอง ก็เป็น Block ที่มี input ต่างๆ เป็น Element ด้วยเหมือนกัน

หากเราสังเกตดีๆ จะเห็นว่าที่ form ทั้ง 2 นั้น มี class ทั้งในบทบาทของ Element และในบทบาทของ Block ดังนั้น เพื่อให้การกำหนดสไตล์มีระบบ ที่ .header__search และ .header__login ให้เราใส่สไตล์โดยมองว่ามันเปนส่วนหนึ่งของ header เช่น สไตล์เกี่ยวกับการจัดตำแหน่ง เป็นต้น ส่วนที่ form ให้เราใส่สไตล์โดยมองว่ามันเป็นกล่องฟอร์มสำหรับกรอกข้อมูล เราจะเห็นว่า class ที่มีบทบาทต่างกัน แม้จะถูกระบุที่ html element เดียวกัน แต่สไตล์ที่กำหนดก็จะไม่เหมือนกัน

เขียน CSS Selectors

เมื่อกำหนด class ทั้งหมดเรียบร้อยแล้ว ก็จะมาถึงขั้นตอนการเขียน css selector แบบนี้

ประโยชน์ของ BEM ก็คือ เพียงแค่เราดู selector คร่าวๆ เราก็จะรู้ได้ทันทีเลยว่า selector เหล่านั้น หมายถึง html element ไหน ทั้งนี้ก็เป็นเพราะว่าเราได้ตั้งชื่อ class อย่างมีระบบนั่นเอง และหากสังเกตดีๆ เราก็จะเห็นว่าการตั้งชื่อ class ด้วยวิธี BEM นั้น จะทำให้เราไม่ต้องใช้  descendant selector เลย ซึ่งมันจะส่งผลดีในแง่ของ performance ไปโดยปริยาย (รายละเอียดเพิ่มเติม สามารถอ่านได้ที่บทความ เขียน CSS Selectors อย่างไรให้มีประสิทธิภาพ ?)

แล้วอย่างนี้ ชื่อ Class จะไม่ยาวเกินไปหรือ ?

แน่นอนว่ามีข้อดีก็ย่อมมีข้อเสีย ซึ่งข้อเสียที่เห็นชัดๆ เลยของ BEM ก็คือชื่อ class ที่ค่อนข้างยาวนั่นเอง แต่ถ้ามาพิจารณาดูดีๆ แล้ว การตั้งชื่อ class แบบ BEM นั้น ดูจะมีประโยชน์มากกว่า จากตัวอย่างก่อนหน้านี้ สมมติว่าเราไม่ได้ใช้วิธี BEM แล้วเราเขียน selector แบบนี้

หากเราไม่ได้ใช้วิธี BEM ถึงแม้ว่าชื่อ class จะสั้นกว่า และมีจำนวน class น้อยลงก็จริง แต่ก็ต้องแลกมาด้วยความซ้ำซ้อนของโค้ด รวมไปถึง descendant selector ลองนึกดูว่าหากเรามีหลายๆ ฟอร์ม การตั้งชื่อ class แบบนี้จะทำให้เราต้องสร้าง css rule ใหม่ขึ้นมาพอสมควร เพราะ css rule ที่มีอยู่เดิมนั้นแทบจะไม่สามารถเอามา reuse ได้เลย การตั้งชื่อ class แบบมีระบบอย่างวิธี BEM จึงดูจะน่าสนใจกว่า

วิธีใช้ Sass กับการเขียนแบบ BEM

sass

หากใครใช้ Sass อยู่แล้ว การใช้ BEM จะยิ่งน่าสนใจเข้าไปอีก เนื่องจาก Sass ได้เพิ่มฟีเจอร์ที่ช่วยอำนวยความสะดวกในการเขียนด้วยวิธี BEM มาโดยเฉพาะ ลองดูตัวอย่างต่อไปนี้ (หากพบว่า Sass ของเรายังไม่รองรับการเขียนแบบด้านล่างนี้ ให้อัพเดท Sass เป็นเวอร์ชันล่าสุดเสียก่อน)

เมื่อนำโค้ด Sass ด้านบนนี้ไป compile เราก็จะได้โค้ด css เหมือนกับที่เราใช้ BEM เลย จะเห็นว่าการรองรับ BEM ของ Sass นั้น ช่วยให้เราไล่โค้ดได้ง่ายขึ้นไปอีก เพราะเราจะดูออกได้อย่างง่ายดายเลยว่าอะไรเป็น Block, Element และ Modifier

อีกประเด็นหนึ่งที่น่าสนใจก็คือ การใช้ BEM จะช่วยลดปัญหาการใช้ nested selector ซ้อนกันมากเกินไปในการเขียน Sass ได้เป็นอย่างดี (คือถึงแม้ว่าโค้ด Sass แบบ BEM จะดูเป็น nested selector ก็จริง แต่พอ compile ออกมาจะไม่มี descendant selector เลย)

บทสรุปการเขียน CSS ด้วยวิธี BEM

การเขียน css โดยใช้การตั้งชื่อ class แบบ BEM นั้นมีข้อดีตรงที่เราจะมีหลักในการตั้งชื่อ ซึ่งทำให้สะดวกในการมาไล่โค้ดในภายหลัง นอกจากนั้น การใช้ BEM ยังมีส่วนช่วยลดความซ้ำซ้อนของโค้ด และทำให้การเขียน selector มีประสิทธิภาพมากยิ่งขึ้น ข้อเสียเพียงอย่างเดียวของ BEM ก็คือชื่อของ class ที่ออกจะยาวและเยอะไปนิด แต่ก็ถือเป็นเรื่องที่เล็กน้อยมากเมื่อเทียบกับประโยชน์ที่เราได้รับจากมัน และหากใครยังสงสัยอยู่ว่า BEM นั้นดีจริงหรือเปล่า ก็ขอให้ดูจากการที่ Sass ถึงกับออกมารองรับการตั้งชื่อ class แบบนี้โดยเฉพาะเลย นั่นก็พอจะทำให้เราเชื่อมั่นได้ว่า BEM น่าจะมีอนาคตที่ดีพอสมควร

(Visited 11,287 times, 4 visits today)

24 Responses to “สอนวิธีเขียน CSS แบบ BEM”

  1. Sappawish Siripon says:

    อ่านจบไปหนึ่งรอบยังคิดว่า get concept ของเรื่องนี้ไม่ครบ 100% แต่มั่นใจว่าต้องเจ๋งแน่ๆ

    ต้องอ่านซ้ำ!!

    ขอบคุณสำหรับบทความดีๆ ครับ :)

  2. Medoza Poodclong says:

    คิดว่าเข้าใจ แต่ทำไมต้อง — __ หรือมันไม่เกี่ยว -*-

  3. ขอบคุณค้าบบ^^

  4. ขอขอบคุณเช่นกันครับ ^_^
    เรื่อง BEM นี้ ผมเองยอมรับเลยว่ามันเข้าใจยากอยู่เหมือนกันครับ
    เราอาจต้องลองลงมือเขียนจริง ก็น่าจะช่วยให้เห็นภาพชัดมากขึ้นครับ

  5. ขอบคุณครับ :)

  6. มันเป็นข้อตกลงของวิธีการตั้งชื่อ class แบบ BEM น่ะครับ ^_^

  7. ขอบคุณสำหรับบทความดีๆ ครับ

  8. Bond BourBon says:

    ขอบคุณคับบบ
    อยากได้วิธีสอนเขียน CSS แบบ SMACSS ด้วยยคับ

  9. NeeOn Idea says:

    ยากจัง T T

  10. ดีมากเลยครับ :) ชอบการแยก code ที่มีระเบียบ :)
    สับสนนิดหน่อย ระหว่าง — และ __ ต้องอ่านวนไปวนมาถึงจะเข้าใจ

    ขอบคุณครับ :)

  11. ดีมากเลยครับ :) ชอบการแยก code ที่มีระเบียบ :)
    สับสนนิดหน่อย ระหว่าง — และ __ ต้องอ่านวนไปวนมาถึงจะเข้าใจ

    ขอบคุณครับ :)

  12. ครับ ตอนแอดมินเขียนก็ปวดหัวเหมือนกันครับ ว่าจะอธิบายยังไงดี
    อาจต้องลองอ่านหลายๆ รอบหน่อย อย่างที่คุณ Ichsarut ว่าครับ แหะๆ

  13. ใครสรุปให้หน่อยได้ไหมครับ
    ยังไม่ค่อย get ว่า __login กับ –login ใช้ต่างกันตอนไหน

  14. แอดมินขออธิบายแบบนี้ได้มั้ยครับ…

    Block คือคำนามครับ
    Element ("__") คือคำนามครับ
    Modifier ("–") คือคำวิเศษณ์ครับ

    form = form
    form__input = input ที่เป็นองค์ประกอบของ form
    form–login = form ที่ทำหน้าที่ login

  15. แอดมินขออธิบายแบบนี้ได้มั้ยครับ…

    Block คือคำนามครับ
    Element ("__") คือคำนามครับ
    Modifier ("–") คือคำวิเศษณ์ครับ

    form = form
    form__input = input ที่เป็นองค์ประกอบของ form
    form–login = form ที่ทำหน้าที่ login

  16. Siam HTML – สังคมของนักพัฒนาเว็บไซต์ในไทย

    .form–search { } /* สไตล์พิเศษของกล่อง form ชนิด search */
    .form–login { } /* สไตล์พิเศษของกล่อง form ชนิด login */

    .form__input–submit { } /* สไตล์ของ input แบบ submit ในกล่อง form */
    .form–search__submit { } /* สไตล์ของปุ่ม submit ในกล่อง form ชนิด search */
    .form–login__submit { } /* สไตล์ของปุ่ม submit ในกล่อง form ชนิด login */

    เข้าใจมาโดยตลอด แต่มางงรู้สึกขัดแย้งเอาตรง
    .header__search { } /* สไตล์ของกล่อง search ที่อยู่ในกล่อง header */
    .header__login { } /* สไตล์ของกล่อง login ที่อยู่ในกล่อง header */
    ทำไมไม่เป็น – ขีดกลาง แต่ใช้ _ ครับ

  17. Bee Nutthanapong
    .header__search = อ้างถึงกล่อง search ที่อยู่ใน header เช่น ใส่ที่ <form> ใน <header>
    .header–search = อ้างถึงกล่อง header เช่น ใส่ที่ <header>
    ประมาณนี้นะครับ

  18. สรุปแล้ว ต้องใช้ควบคู่กับ Sass ถึงจะลดเวลาจริงๆ

  19. ทำไมต้อง สองขีด ใช้ ขีดเดียวได้ไหมครับ

  20. ต้อง 2 ขีดเท่านั้นครับ ขีดเดียวคนอื่นจะไม่แน่ใจว่านี่เราใช้ BEM หรือตั้งชื่อแบบทั่วไปฮะ

  21. ที่ต้องมีสองขีดเพราะ แบบขีดเดียวมันเอาไว้ตั้งชื่อปกติแบบเว้นวรรคครับ
    เพราะเวลาตั้งชื่อนิยมตั้งตัวเล็กหมด เช่น mynameisinput ดังนั้นเลยมี
    ขีดเดียวเพื่อให้อ่านง่ายเป็น my-name-is-input หรือ my_name_is_input

    เอ่อผมคิดว่าแบบนี้นะ ใครเขียน html css โปรๆ มาคอนเฟิร์มหน่อยครับว่าใช่ไหม

  22. ตามนี้เลยฮะ

  23. อ่านแล้วเข้าใจเลยค่ะ ขอบคุณค่ะ

Leave a Reply