พอร์ทัลสำหรับโมดัลและทูลทิป
ใช้ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- คอมโพเนนต์แบบประกอบร่วมกับ Context
- พร็อพส์สำหรับเรนเดอร์และรูปแบบ HOC
- พอร์ทัลสำหรับโมดัลและทูลทิป
- ขอบเขตข้อผิดพลาด