البحث في المدونة

العودة لجميع المقالات
أنظمة التصميم و APEX Theme Factory

سر الوميض الصفري: القضاء على الفلاش الأبيض بتمهيد Page 0 في Oracle APEX ⚡🎭

الجزء الثالث من سلسلة APEX Theme Factory. إزاي نقضي تماماً على الوميض الأبيض المزعج في النمط المظلم باستخدام كود تمهيد متزامن في Page 0 وحل لغز النوافذ المنبثقة.

فهرس المحتويات
  1. سر الوميض الصفري: القضاء على الفلاش الأبيض بتمهيد Page 0 في Oracle APEX ⚡🎭
  2. ⚡ الخلاصة في كلمتين (TL;DR)
  3. ⚡ التشريح الدقيق للوميض المزعج (FOUC)
  4. 📜 محرك التمهيد السريع (2 ميلي ثانية)
  5. ليه السطر ده خارق ومحصن من الأخطاء؟
  6. 🎭 الفخ الخفي في النوافذ المنبثقة (Modal Dialogs)
  7. الحل الهندسي: حقن متزامن في موضعين مكملين
  8. 🛡️ طريقة التركيب اليدوي جوه Page Designer
  9. ⏭️ المقال القادم: ماكينة الحزم المحمولة

🇬🇧 English Version: Read the original article in English here.

سر الوميض الصفري: القضاء على الفلاش الأبيض بتمهيد Page 0 في Oracle APEX ⚡🎭

الجزء الثالث من السلسلة التقنية الشاملة: تسريع الأداء ومنع وميض الشاشات المزعج في Oracle APEX.


⚡ الخلاصة في كلمتين (TL;DR)

تخيل رايح تحضر العرض الأول لمسرحية غامضة ومرعبة في دار الأوبرا، والجمهور قاعد في الصالة في هدوء وظلام تام مستني العرض يبدأ. فجأة، عامل المسرح يشد الستارة المخملية التقيلة قبل ما يظبط الإضاءة.. والمسرح كان كحل!

بعدها بثانيتين، يقوم طاقم العمال ضارب كشافات نيون بيضاء عملاقة بقوة 50,000 شمعة في وشوش الجمهور مباشرة، وبعدين يجروا يركبوا جيلاتين أزرق وداكن على الكشافات عشان يعملوا الجو المرعب! الصدمة دي بتعمي عيون الجمهور وتجيب لهم صداع نصفي قبل ما الممثلين ينطقوا كلمة واحدة!

الصدمة البصرية دي هي بالظبط اللي بيعاني منها مستخدمين نظامك لما تعمل ثيم داكن باستخدام الأوامر الديناميكية (Dynamic Actions) أو أكواد $(document).ready(). المتصفح بيرسم خلفية نمط Iris البيضاء الناصعة الأول، ويستنى الصفحة تخلص تحميل، وبعدين يشغل كود الجافاسكريبت عشان يقلب الصفحة سودة! النتيجة؟ وميض أبيض مؤلم للعين لمدة 200 ميلي ثانية مع كل دوسة ماوس!

في الجزء التالت من سلسلتنا APEX Theme Factory، هنقضي على الوميض المزعج (FOUC) للأبد. هنتعلم سر الوميض الصفري: إزاي بنحقن سطر كود متزامن فائق الخفة في الصفحة 0 (Page 0) بيظبط إضاءة المشهد في أقل من 2 ميلي ثانية قبل ما المتصفح يرسم أول بكسل على الشاشة، ومع تغطية كاملة للصفحات العادية والنوافذ المنبثقة (Modal Dialogs) معاً. 🚀

📦 مستودع كود المشروع: كود المحرك، وحزم الثيمات، وسكربتات التثبيت متاحة بالكامل على github.com/ash2osh/APEX_THEME_FACTORY.


⚡ التشريح الدقيق للوميض المزعج (FOUC)

عشان نفهم ليه أساليب التبديل التقليدية بتعمل فلاش ووميض مزعج، لازم نبص على مسار المعالجة والرسم جوه المتصفح (Critical Rendering Path):

❌ كابوس الوميض غير المتزامن (Dynamic Actions / document.ready)
───────────────────────────────────────────────────────────────────────────
1. طلب الصفحة GET /ords/r/demo/ut/dashboard
2. استلام ملف الـ HTML وتطبيق الـ CSS الافتراضي (خلفية Iris فاتحة #fbf9f8)
3. 💥 الرسم الأول (First Paint): المتصفح يرسم الشاشة باللون الأبيض الناصع!
4. اكتمال بناء شجرة الصفحة (DOMContentLoaded)
5. تشغيل كود JavaScript: قراءة التفضيل وإضافة كلاس .app-theme-carbon-volt
6. 🔄 إعادة الرسم (Second Paint): المتصفح يعيد الحسابات ويقلب الشاشة داكنة (#12151c)
── المدة الإجمالية للوميض: من 150 لـ 350 ميلي ثانية بتخطف البصر ──────────

✅ سر الوميض الصفري في Theme Factory (تشغيل متزامن في Page 0)
───────────────────────────────────────────────────────────────────────────
1. طلب الصفحة GET /ords/r/demo/ut/dashboard
2. المتصفح يقابل كود التمهيد المتزامن فوراً في Page 0
3. ⚡ كود سريع في أقل من 2 ميلي ثانية يقرا التفضيل ويحط الكلاس على <html>
4. المتصفح يكمل قراءة باقي الصفحة والكلاس الداكن راكب بالفعل
5. 🎨 الرسم الأول (First Paint): المتصفح يرسم النمط المظلم مباشرة من أول فريم!

لما بتعتمد على إعداد Execute when Page Loads أو الأوامر الديناميكية في Page Designer، كودك بيتنفذ في الخطوة رقم 5، بعد ما المتصفح يكون رسم الشاشة بالكامل باللون الأبيض في الخطوة 3!

عشان نمنع الوميض ده بدون ما نحمل على السيرفر، لازم الكود بتاعنا يتنفذ بشكل متزامن (Synchronously) أثناء قراءة الـ HTML وقبل أول لقطة رسم للمتصفح.


📜 محرك التمهيد السريع (2 ميلي ثانية)

في Theme Factory بنستخدم سطر تمهيد خفيف ومجرب وموجود جوه القالب الرسمي للمشروع في الملف installer/templates/bootstrap.html.tmpl:

<!-- APEX_THEME_FACTORY_MANAGED:BEGIN:BOOTSTRAP -->
<script>
window.APEX_THEME_FACTORY_CONFIG = {
  appId: 102,
  defaultTheme: "linen",
  switcherEnabled: true,
  themes: [
    {"name": "carbon-volt", "title": "Carbon Volt"},
    {"name": "citrus-pop", "title": "Citrus Pop"},
    {"name": "solarized-dark", "title": "Solarized Dark"},
    {"name": "linen", "title": "Linen"}
  ]
};

(function (doc, config) {
  "use strict";
  var root = doc.documentElement;
  var key = "apex.themeFactory." + config.appId;
  var allowed = ["iris"].concat(config.themes.map(function (theme) { return theme.name; }));
  var selected = config.defaultTheme;

  if (config.switcherEnabled) {
    try {
      selected = window.localStorage.getItem(key) || selected;
      /* مسح أي قيم قديمة أو محذوفة من التخزين المحلي */
      if (allowed.indexOf(selected) === -1) {
        window.localStorage.removeItem(key);
        selected = config.defaultTheme;
      }
    } catch (e) {
      /* معالجة وضع التصفح الخفي في سفاري */
      selected = config.defaultTheme;
    }
  }

  /* إزالة أي كلاس ثيم قديم وإضافة الكلاس المعتمد الحالي */
  Array.prototype.slice.call(root.classList).forEach(function (name) {
    if (name.indexOf("app-theme-") === 0) { root.classList.remove(name); }
  });

  if (selected !== "iris") { 
    root.classList.add("app-theme-" + selected); 
  }
  
  root.dataset.appThemeDefault = config.defaultTheme;
  root.dataset.appThemeCurrent = selected;
}(document, window.APEX_THEME_FACTORY_CONFIG));
</script>
<!-- APEX_THEME_FACTORY_MANAGED:END:BOOTSTRAP -->

ليه السطر ده خارق ومحصن من الأخطاء؟

  1. بدون أي مكتبات خارجية: بيعتمد على جافاسكريبت أصلي خام (Pure Vanilla ES5). مش بيستنى jQuery ولا دوال أبيكس ولا ملفات الخطوط.
  2. سرعة التنفيذ: في كل الاختبارات القياسية على كروم وفايرفوكس وسفاري، زمن تشغيل الكود ده بيتراوح بين 0.8 ميلي ثانية إلى 1.8 ميلي ثانية فقط!
  3. تنظيف التخزين التلقائي: لو المستخدم كان مخزن ثيم قديم تم حذفه من التطبيق، الكود بيمسح المفتاح التالف تلقائياً ويرجع للثيم الافتراضي config.defaultTheme.
  4. حماية وضع التصفح الخفي (Safari Private Browsing): في متصفح سفاري بالوضع الخاص، محاولة الوصول لـ localStorage أحياناً بترمي خطأ أمني (QUOTA_EXCEEDED_ERR). كود الـ try/catch بيعالج ده بسلاسة ويشغل الثيم الافتراضي بدون أي توقف.

🎭 الفخ الخفي في النوافذ المنبثقة (Modal Dialogs)

ودلوقتي مع اللغز اللي بيوقع 99% من مطوري أبيكس لما يركبوا كود تمهيد في الشاشات.

المطور بيفتح Page 0 (Global Page) ويعمل منطقة محتوى ثابت (Static Content Region) ويحط فيها الكود، ويختار موضع القالب: #BANNER# (أو رأس الصفحة Top of Page).

يجرب الشاشات والتقارير الرئيسية العادية.. يلاقي النمط المظلم شغال بدون وميض وبسرعة الصاروخ، ويفرح جداً!

وفجأة، مستخدم يدوس على زرار يفتح نافذة منبثقة (Modal Dialog) عشان يعدل بيانات عميل..

بوم! 💥 شاشة بيضاء بتنفجر في وشه كأن فلاش كاميرا ضرب في عينيه!

┌────────────────────────────────────────────────────────┐
│  قالب الصفحات العادية في Universal Theme 42            │
│  [#HEADER#]                                            │
│  [#BANNER#]  <-- الكود بيتنفذ بنجاح والصفحة بتتلون     │
│  [#BODY#]                                              │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│  قالب النوافذ المنبثقة (Modal Dialog Page)             │
│  [#HEADER#]                                            │
│  منفذ #BANNER# غير موجود أصلاً في قالب النوافذ!        │
│  [#BODY#]                                              │
│  النتيجة: الكود مش بيتنفذ خالص والنافذة بتفضل بيضاء!   │
└────────────────────────────────────────────────────────┘

السر في Universal Theme 42 هو إن قوالب النوافذ المنبثقة (Modal Dialogs) لا تحتوي على الموضع #BANNER# إطلاقاً!

وبما إن النافذة المنبثقة بتشتغل جوه إطار مستقل (<iframe>) وله جذر وثيقة خاص بيه، لو الكود محطوط في #BANNER# بس، النافذة مش هتاخد كلاس الثيم أبداً وهتفتح بيضاء زي اللوح!

الحل الهندسي: حقن متزامن في موضعين مكملين

في Theme Factory بنحل المعضلة دي بإنشاء منطقتين في الصفحة 0:

معرف المنطقة موضع القالب المختار نوع القالب المستهدف الدور والغرض
theme_factory_bootstrap #BANNER# قوالب الصفحات العادية يغطي الشاشات والتقارير الرئيسية
theme_factory_bootstrap_dialog #REGION_POSITION_01# قوالب النوافذ المنبثقة يغطي كل النوافذ المنبثقة والـ Wizards

المنطقتين شايلين نفس كود التمهيد. لما الصفحة ترن:

  • لو صفحة عادية: موضع #BANNER# بيتنفذ وموضع الحوار بيكون خامل.
  • لو نافذة منبثقة: موضع #REGION_POSITION_01# بيتنفذ فوراً جوه الـ iframe.

تغطية شاملة بنسبة 100%، وبدون أي وميض أبيض في أي بكسل في التطبيق!


🛡️ طريقة التركيب اليدوي جوه Page Designer

لو عايز تركب الكود ده يدوي جوه شاشة تصميم الصفحات في أوراكل أبيكس:

الخطوة 1: افتح الصفحة رقم 0 (Shared Components -> Global Page).
الخطوة 2: أنشئ منطقة جديدة (Create Region):
         - العنوان: Theme Factory Bootstrap
         - النوع: Static Content
         - القالب: Blank with Attributes (بدون قالب خارجي)
         - موضع التخطيط: Region Position 01 (أو Banner حسب الصفحة)
الخطوة 3: حط كود الـ <script> في مربع Source HTML.
الخطوة 4: في خانة شروط السيرفر (Server-side Condition) كالتالي:
         - خليها None (يعني المنطقة تشتغل دايماً في كل الصفحات).

(ملحوظة: لو بتستخدم أداة التثبيت الآلية بالأوامر اللي هنشرحها في الجزء الرابع، الأداة بتبرمج المنطقتين تلقائياً وبدون أي تدخل يدوي منك!)


⏭️ المقال القادم: ماكينة الحزم المحمولة

دلوقتي عندنا قلعة CSS حصينة، وتمهيد فوري بيقضي على الوميض في أقل من 2 ميلي ثانية. بس السؤال: إزاي نجمع الكلام ده كله في حزمة واحدة يقدر أي مطور يثبتها بضغطة زر في 50 تطبيق في ثواني؟

في الجزء الرابع من السلسلة، هنشرح ماكينة الحزم المحمولة: ملفات ZIP المعزولة، وإدارة خطوط WOFF2 بدون اعتمادات خارجية، وتعديل ملفات APEXLang بضمانات التراجع الذري! 📦🚀


💡 ابدأ مع APEX Theme Factory الآن: تقدر تعمل Clone للمستودع، وتجرب الثيمات الجاهزة (carbon-volt، iris-dark، وغيرها)، وتستخدم أداة التثبيت الآلية: 🔗 https://github.com/ash2osh/APEX_THEME_FACTORY

أحمد شريف
أحمد شريف / ash2osh

أدلة وشروحات وملاحظات هندسية.