กรณีศึกษา :has()

Swetha Gopalakrishnan
Swetha Gopalakrishnan
Saurabh Rajpal
Saurabh Rajpal

CSS ขึ้นชื่อว่าไม่มีวิธีเลือกองค์ประกอบระดับบนสุดโดยตรงตามองค์ประกอบระดับล่าง ซึ่งเป็นคำขอที่นักพัฒนาซอฟต์แวร์ขอมามากที่สุดเป็นเวลาหลายปี ตัวเลือก :has() ซึ่งตอนนี้เบราว์เซอร์หลักทั้งหมดรองรับแล้วจะช่วยแก้ปัญหานี้ได้ ก่อนที่จะมี :has() คุณมักจะต้องเชื่อมโยงตัวเลือกยาวๆ หรือเพิ่มคลาสสำหรับฮุกการจัดรูปแบบ ตอนนี้คุณสามารถจัดรูปแบบตามความสัมพันธ์ขององค์ประกอบกับองค์ประกอบที่สืบทอดมาได้แล้ว อ่านเพิ่มเติม เกี่ยวกับตัวเลือก :has() ใน CSS Wrapped 2023 และ 5 CSS Snippets Every Frontend Developer Should Know

แม้ว่าตัวเลือกนี้จะดูเล็กน้อย แต่ก็ช่วยให้เกิดกรณีการใช้งานจำนวนมากได้ บทความนี้แสดงกรณีการใช้งานบางส่วนที่บริษัทอีคอมเมิร์ซใช้ประโยชน์จากตัวเลือก :has()

ดูซีรีส์ฉบับเต็มที่บทความนี้เป็นส่วนหนึ่ง ซึ่งจะพูดถึงวิธีที่ บริษัทอีคอมเมิร์ซปรับปรุงเว็บไซต์โดยใช้ฟีเจอร์ CSS และ UI ใหม่

Policybazaar

ตัวเลือก :has() ช่วยให้เราไม่ต้องใช้การตรวจสอบการเลือกของผู้ใช้ด้วย JavaScript และแทนที่ด้วยโซลูชัน CSS ซึ่งทำงานได้อย่างราบรื่น และให้ประสบการณ์การใช้งานแบบเดิม—Aman Soni, Tech Lead, Policybazaar

ทีมการลงทุนของ Policybazaar ใช้ตัวเลือก :has() อย่างชาญฉลาดเพื่อแสดงการบ่งชี้ด้วยภาพที่ชัดเจนสำหรับผู้ใช้ที่เปรียบเทียบแพ็กเกจ รูปภาพต่อไปนี้แสดงแพ็กเกจ 2 ประเภทภายใน UI การเปรียบเทียบ (สีเหลืองและสีน้ำเงิน) แพ็กเกจแต่ละประเภทจะเปรียบเทียบได้กับแพ็กเกจประเภทเดียวกันเท่านั้น การใช้ :has() จะทำให้ผู้ใช้เลือกแพ็กเกจประเภทอื่นไม่ได้เมื่อเลือกแพ็กเกจประเภทหนึ่ง

การใช้ :has() เพื่อจัดรูปแบบองค์ประกอบระดับบนสุดและองค์ประกอบระดับล่างเพื่อสร้างฟังก์ชันการเลือกที่ผูกกับหมวดหมู่

รหัส

:has() ช่วยให้คุณจัดรูปแบบองค์ประกอบระดับบนสุดและองค์ประกอบระดับล่างได้ โค้ดต่อไปนี้จะตรวจสอบว่าคอนเทนเนอร์ระดับบนสุดมีการตั้งค่าคลาส .disabled-group หรือไม่ หากมีการตั้งค่าไว้ การ์ดจะเป็นสีเทา และปุ่ม "เพิ่ม" จะไม่ตอบสนองต่อการคลิกโดยการตั้งค่า pointer-events เป็น none

.plan-group-container:has(.disabled-group) {
  opacity: 0.5;
  filter: grayscale(100%);
}

.plan-group-container:has(.disabled-section) .button {
  pointer-events: none;
  border-color: #B5B5B5;
  color: var(--text-primary-38-color);
  background: var(--input-border-color);
}

ทีมสุขภาพของ Policybazaar ใช้กรณีการใช้งานที่แตกต่างออกไปเล็กน้อย ทีมนี้มีแบบทดสอบแบบอินไลน์สำหรับผู้ใช้และใช้ :has() เพื่อตรวจสอบสถานะช่องทำเครื่องหมายของคำถามเพื่อดูว่ามีการตอบคำถามหรือไม่ หากมีการตอบคำถาม ระบบจะใช้ภาพเคลื่อนไหวเพื่อเปลี่ยนไปแสดงคำถามถัดไป

health.policybazaar.com/

รหัส

ในตัวอย่างการเปรียบเทียบแพ็กเกจ มีการใช้ :has() เพื่อตรวจสอบการมีอยู่ของคลาส นอกจากนี้ คุณยังตรวจสอบสถานะขององค์ประกอบอินพุต เช่น ช่องทำเครื่องหมายได้โดยใช้ :has(input:checked) ในภาพที่แสดงแบบทดสอบ คำถามแต่ละข้อในแบนเนอร์สีม่วงเป็นช่องทำเครื่องหมาย Policybazaar จะตรวจสอบว่ามีการตอบคำถามแล้วหรือไม่โดยใช้ :has(input:checked) และหากมีการตอบแล้ว ให้ทริกเกอร์ภาพเคลื่อนไหวโดยใช้ animation: quesSlideOut 0.3s 0.3s linear forwards เพื่อเลื่อนไปแสดงคำถามถัดไป ดูวิธีการทำงานได้ในโค้ดต่อไปนี้

.segment_banner__wrap__questions {
 position: relative;
 animation: quesSlideIn 0.3s linear forwards;
}

.segment_banner__wrap__questions:has(input:checked) {
 animation: quesSlideOut 0.3s 0.3s linear forwards;
}


@keyframes quesSlideIn {
 from {
   transform: translateX(50px);
   opacity: 0;
 }
 to {
   transform: translateX(0px);
   opacity: 1;
 }
}

@keyframes quesSlideOut {
 from {
   transform: translateX(0px);
   opacity: 1;
 }
 to {
   transform: translateX(-50px);
   opacity: 0;
 }
}

Tokopedia

Tokopedia ใช้ :has() เพื่อสร้างรูปภาพซ้อนทับหากภาพขนาดย่อของผลิตภัณฑ์มีวิดีโอ หากภาพขนาดย่อของผลิตภัณฑ์มีคลาส .playIcon ระบบจะเพิ่มการซ้อนทับ CSS ในที่นี้ ตัวเลือก :has() ใช้ร่วมกับตัวเลือกการซ้อน & ภายในคลาส .thumbnailWrapper ที่ครอบคลุม ซึ่งใช้กับภาพขนาดย่อทั้งหมด วิธีนี้จะสร้าง CSS ที่เป็นโมดูลาร์และอ่านง่ายขึ้น

ภาพหน้าจอของหน้า Tokopedia ก่อนและหลังการใช้ตัวเลือก has
ก่อนและหลังการใช้ :has()

รหัส

โค้ดต่อไปนี้ใช้ ตัวเลือกและตัวรวม CSS (& และ >) และการซ้อนกับ :has() เพื่อจัดรูปแบบภาพขนาดย่อ สำหรับเบราว์เซอร์ที่ไม่รองรับ ระบบจะใช้กฎคลาส CSS เพิ่มเติมทั่วไปเป็นตัวเลือกสำรอง นอกจากนี้ยังใช้กฎ @supports selector(:has(*)) เพื่อตรวจสอบการรองรับเบราว์เซอร์ด้วย ดังนั้น ประสบการณ์การใช้งานโดยรวมจึงเหมือนกันในเบราว์เซอร์ทุกเวอร์ชัน

export const thumbnailWrapper = css`
  padding: 0;
  margin-right: 7px;
  border: none;
  outline: none;
  background: transparent;

  > div {
    width: 64px;
    height: 64px;
    overflow: hidden;
    cursor: pointer;
    border-color: ;
    position: relative;
    border: 2px solid ${NN0};
    border-radius: 8px;
    transition: border-color 0.25s;

    &.active {
      border-color: ${GN500};
    }

    @supports selector(:has(*)) {
      &:has(.playIcon) {
        &::after {
          content: '';
          display: block;
          background: rgba(0, 0, 0, 0.2);
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
        }
      }
    }

    & > .playIcon {
      position: absolute;
      top: 25%;
      left: 25%;
      width: 50%;
      height: 50%;
      text-align: center;
      z-index: 1;
    }
  }
`;

สิ่งที่ควรพิจารณาเมื่อใช้ :has()

รวม :has() กับตัวเลือกอื่นๆ เพื่อสร้างเงื่อนไขที่ซับซ้อนมากขึ้น ดูตัวอย่างได้ใน has() the family selector

แหล่งข้อมูล

สำรวจบทความอื่นๆ ในซีรีส์นี้ซึ่งพูดถึงวิธีที่บริษัทอีคอมเมิร์ซได้รับประโยชน์จากการใช้ฟีเจอร์ CSS และ UI ใหม่ๆ เช่น ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน การเปลี่ยนมุมมอง ป๊อปโอเวอร์ และการค้นหาคอนเทนเนอร์