0Pricing
Frontend Academy · บทเรียน

พอร์ทัลสำหรับโมดัลและทูลทิป

ใช้ ReactDOM.createPortal เพื่อเรนเดอร์คอมโพเนนต์ลูกลงในโหนด DOM อื่น ทำให้สร้างโมดัลและทูลทิปที่หลุดออกจากคอนเทนเนอร์ overflow:hidden ได้

พอร์ทัลสำหรับโมดัลและทูลทิป เป็นบทเรียน Frontend Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Frontend Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Frontend Academy มีบทเรียนทั้งหมด 4 บทเรียน

ปัญหาการหลุดออกจากคอนเทนเนอร์

โมดัลและคำแนะนำแบบป๊อปอัปต้องเรนเดอร์ที่ด้านบนสุดของ DOM เพื่อหลุดออกจาก overflow:hidden การซ้อนชั้นของ z-index และบริบทที่มีรากจาก transform ของคอมโพเนนต์แม่ วิธีแก้คือใช้พอร์ทัล

API ของ createPortal

createPortal(children, container) จะเรนเดอร์ children ลงใน container ซึ่งเป็นโหนด DOM ใดก็ได้ ขณะเดียวกันยังคงให้สิ่งเหล่านั้นเป็นคอมโพเนนต์ลูกในเชิงตรรกะของคอมโพเนนต์รีแอ็กต์ เพื่อให้เหตุการณ์และบริบททำงานได้

import { createPortal } from 'react-dom';

function Modal({ children, onClose }) {
  return createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal-content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

คอนเทนเนอร์ของพอร์ทัล

เป้าหมายที่ใช้กันทั่วไป ได้แก่ document.body ดิวเฉพาะ #modal-root ใน index.html หรือคอนเทนเนอร์ที่สร้างขึ้นเมื่อจำเป็นและเชื่อมกับการอ้างอิง

<!-- index.html -->
<div id="root"></div>
<div id="modal-root"></div>

// component:
const modalRoot = document.getElementById('modal-root');
return createPortal(<div>...</div>, modalRoot);

เหตุใดจึงไม่เรนเดอร์ไว้ตรงนั้น

หากคอมโพเนนต์แม่มี overflow: hidden โมดัลลูกจะถูกตัด หากคอมโพเนนต์แม่มี transform: scale การจัดตำแหน่งแบบคงที่ของโมดัลจะอ้างอิงกับคอมโพเนนต์แม่ พอร์ทัลช่วยหลีกเลี่ยงปัญหาเหล่านี้ทั้งหมด

เหตุการณ์ยังคงลอยขึ้นตามลำดับชั้น

แม้จะเรนเดอร์ที่อื่นใน DOM แต่เหตุการณ์ของรีแอ็กต์จะลอยขึ้นตามต้นไม้คอมโพเนนต์ ไม่ใช่ต้นไม้ DOM การคลิกในพอร์ทัลยังคงลอยขึ้นไปยังตัวจัดการ onClick ของคอมโพเนนต์รีแอ็กต์บรรพบุรุษ

ปิดเมื่อคลิกพื้นหลังทับ

หยุดการเผยแพร่เหตุการณ์บนเนื้อหาโมดัล เพื่อไม่ให้การคลิกภายในปิดโมดัล

function Modal({ onClose, children }) {
  return createPortal(
    <div className="backdrop" onClick={onClose}>
      <div className="content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

ปิดเมื่อกดปุ่ม Escape

เพิ่มตัวรับฟังเหตุการณ์ keydown ขณะที่โมดัลเปิดอยู่ และนำออกเมื่อถอดคอมโพเนนต์

useEffect(() => {
  const onKey = (e) => { if (e.key === 'Escape') onClose(); };
  window.addEventListener('keydown', onKey);
  return () => window.removeEventListener('keydown', onKey);
}, [onClose]);

การจัดการโฟกัสในพอร์ทัล

เมื่อโมดัลเปิด ให้ย้ายโฟกัสเข้าไปด้านใน กักโฟกัสไว้จนกว่าจะปิด แล้วคืนโฟกัสไปยังองค์ประกอบที่เปิดโมดัล ใช้ไลบรารีอย่าง focus-trap-react เพื่อหลีกเลี่ยงการเขียนความสามารถนี้ขึ้นใหม่

องค์ประกอบ dialog ดั้งเดิม

<dialog> ของ HTML จัดการการวางตำแหน่งโมดัล พื้นหลังทับ การกักโฟกัส และการปิดด้วย Escape ให้โดยดั้งเดิม สำหรับโมดัลส่วนใหญ่ นี่เป็นจุดเริ่มต้นที่ดีกว่าการใช้พอร์ทัล

function Modal({ open, onClose, children }) {
  const ref = useRef(null);
  useEffect(() => {
    if (open) ref.current?.showModal();
    else ref.current?.close();
  }, [open]);
  return (
    <dialog ref={ref} onClose={onClose}>
      {children}
    </dialog>
  );
}

คำแนะนำแบบป๊อปอัปและป๊อปโอเวอร์

คำแนะนำแบบป๊อปอัปจะจัดตำแหน่งโดยอ้างอิงกับองค์ประกอบหลัก พอร์ทัลช่วยให้สิ่งเหล่านี้หลุดออกจากบริบทการซ้อนชั้น ใช้ร่วมกับ floating-ui เพื่อตรวจจับการชน การพลิกตำแหน่ง และการจัดวาง

import { useFloating, autoUpdate } from '@floating-ui/react';

function Tooltip({ children, content }) {
  const { refs, floatingStyles } = useFloating({ whileElementsMounted: autoUpdate });
  const [open, setOpen] = useState(false);
  return (
    <>
      <button ref={refs.setReference} onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
        {children}
      </button>
      {open && createPortal(
        <div ref={refs.setFloating} style={floatingStyles}>{content}</div>,
        document.body
      )}
    </>
  );
}

ข้อควรระวังเกี่ยวกับ SSR

เอกสารจะไม่มีอยู่บนเซิร์ฟเวอร์ ให้เรนเดอร์เฉพาะฝั่งไคลเอนต์โดยตรวจสอบชนิดของ window หรือใช้ dynamic ของ Next.js พร้อม ssr:false

การเข้าถึง: aria-modal

คอนเทนเนอร์โมดัลควรมี role="dialog", aria-modal="true" และ aria-labelledby ที่ชี้ไปยังหัวเรื่องของโมดัล หรือใช้ <dialog> ดั้งเดิม

ตรวจสอบอย่างรวดเร็ว

เหตุใดจึงใช้ createPortal กับโมดัล แทนการเรนเดอร์โมดัลไว้โดยตรงในต้นไม้คอมโพเนนต์

ทบทวน: พอร์ทัล

createPortal(children, container) จะเรนเดอร์ DOM ที่อื่น ขณะเดียวกันยังคงโครงสร้างต้นไม้รีแอ็กต์ในเชิงตรรกะไว้ ช่วยแก้ปัญหาการหลุดออกจาก overflow, transform และ z-index เหตุการณ์ยังคงลอยขึ้นตามต้นไม้รีแอ็กต์ เพิ่มการปิดด้วย Escape และการจัดการโฟกัส องค์ประกอบ <dialog> ดั้งเดิมจัดการเรื่องเหล่านี้ได้มากสำหรับโมดัล ใช้ร่วมกับ floating-ui สำหรับคำแนะนำแบบป๊อปอัป และอย่าลืม SSR โดยตรวจสอบชนิดของ window

คำถามที่พบบ่อย

บทเรียน “พอร์ทัลสำหรับโมดัลและทูลทิป” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “พอร์ทัลสำหรับโมดัลและทูลทิป” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Frontend Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Frontend Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “พอร์ทัลสำหรับโมดัลและทูลทิป”

ใช้ ReactDOM.createPortal เพื่อเรนเดอร์คอมโพเนนต์ลูกลงในโหนด DOM อื่น ทำให้สร้างโมดัลและทูลทิปที่หลุดออกจากคอนเทนเนอร์ overflow:hidden ได้ คุณปฏิบัติ Frontend Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Frontend Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Frontend Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “พอร์ทัลสำหรับโมดัลและทูลทิป” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Frontend Academy นี้ได้ไหม

ได้ บทเรียน Frontend Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. คอมโพเนนต์แบบประกอบร่วมกับ Context
  2. พร็อพส์สำหรับเรนเดอร์และรูปแบบ HOC
  3. พอร์ทัลสำหรับโมดัลและทูลทิป
  4. ขอบเขตข้อผิดพลาด
← กลับไปที่ Frontend Academy