عنصران أساسيان في نظام Web Components الحديث:
<template>: • يحتوي HTML لا يُعرض ولا يُنفَّذ عند تحميل الصفحة • محتواه مخفي حتى يُستنسخ بـ JavaScript • مثالي لـ: بطاقات متكررة، قوالب محتوى، Web Components • الاستنساخ بـ content.cloneNode(true) • المحتوى داخله لا يُحمَّل (لا صور، لا CSS، لا سكريبتات)
<slot>: • "فتحة" داخل Web Component تقبل محتوى مُمرَّراً • يُستخدم فقط داخل Shadow DOM • name صفة للـ named slots • المحتوى الافتراضي بين وسميه يظهر عند عدم تمرير محتوى • الأساس لقابلية إعادة الاستخدام في Web Components',
| 1 | <!-- template: قالب HTML مخفي --> |
| 2 | <template id="course-card-template"> |
| 3 | <div class="card"> |
| 4 | <img class="card-img" src="" alt=""> |
| 5 | <div class="card-body"> |
| 6 | <h3 class="card-title"></h3> |
| 7 | <p class="card-price"></p> |
| 8 | <button class="card-btn">سجّل الآن</button> |
| 9 | </div> |
| 10 | </div> |
| 11 | </template> |
| 12 | |
| 13 | <script> |
| 14 | // استنساخ واستخدام القالب |
| 15 | const template = document.getElementById('course-card-template'); |
| 16 | const courses = [ |
| 17 | { title: 'HTML', price: 'مجاني', img: '/html.jpg' }, |
| 18 | { title: 'CSS', price: '99 جنيه', img: '/css.jpg' }, |
| 19 | ]; |
| 20 | |
| 21 | courses.forEach(course => { |
| 22 | const clone = template.content.cloneNode(true); |
| 23 | clone.querySelector('.card-title').textContent = course.title; |
| 24 | clone.querySelector('.card-price').textContent = course.price; |
| 25 | clone.querySelector('.card-img').src = course.img; |
| 26 | document.body.appendChild(clone); |
| 27 | }); |
| 28 | </script> |
| 29 | |
| 30 | <!-- slot: داخل Web Component --> |
| 31 | <my-card> |
| 32 | <span slot="title">عنوان مخصص</span> |
| 33 | <p slot="body">محتوى مخصص للبطاقة</p> |
| 34 | </my-card> |
مثال 1: Web Component بسيط مع template وslot
| 1 | <!-- تعريف القالب --> |
| 2 | <template id="user-badge-template"> |
| 3 | <style> |
| 4 | .badge { display: flex; gap: 10px; align-items: center; |
| 5 | padding: 12px; border: 1px solid #eee; border-radius: 8px; } |
| 6 | .avatar { width: 48px; height: 48px; border-radius: 50%; } |
| 7 | .info { display: flex; flex-direction: column; } |
| 8 | </style> |
| 9 | <div class="badge"> |
| 10 | <img class="avatar" src="" alt=""> |
| 11 | <div class="info"> |
| 12 | <slot name="name"><strong>مستخدم مجهول</strong></slot> |
| 13 | <slot name="role"><small>مستخدم</small></slot> |
| 14 | </div> |
| 15 | </div> |
| 16 | </template> |
| 17 | |
| 18 | <script> |
| 19 | class UserBadge extends HTMLElement { |
| 20 | connectedCallback() { |
| 21 | const shadow = this.attachShadow({ mode: 'open' }); |
| 22 | const tmpl = document.getElementById('user-badge-template'); |
| 23 | shadow.appendChild(tmpl.content.cloneNode(true)); |
| 24 | |
| 25 | const avatar = shadow.querySelector('.avatar'); |
| 26 | avatar.src = this.getAttribute('avatar') || '/default-avatar.png'; |
| 27 | avatar.alt = this.getAttribute('name') || 'مستخدم'; |
| 28 | } |
| 29 | } |
| 30 | customElements.define('user-badge', UserBadge); |
| 31 | </script> |
| 32 | |
| 33 | <!-- الاستخدام --> |
| 34 | <user-badge avatar="/ahmed.jpg" name="أحمد"> |
| 35 | <span slot="name">أحمد محمد</span> |
| 36 | <small slot="role">مطور ويب</small> |
| 37 | </user-badge> |