يمكن للعنصر أن يحصل على التركيز من المستخدم إمّا بالنقر عليه مباشرة أو باستعمال المفتاح Tab
من لوحة المفاتيح، ويمكنه أيضا أن يحصل عليه افتراضيًّا عند تحميل الصفحة بواسطة السمة autofocus
في HTML، إلى غير ذلك من وسائل الحصول على التركيز.
يُقصد بالتركيز على العنصر عموما "التهيّء لاستقبال المُعطيات"، وبذلك فإنّه الوقت المناسب لإجراء الشيفرة التي تعمل على تهيئة الوظائف المطلوبة.
قد تكون لحظة فقدان التركيز (أو ما يُعرف بـ "blur" أي "الضبابيّة") أكثر أهميّة. وتحصل عندما ينقر المستخدم في مكان آخر أو يضغط على المفتاح Tab
لينتقل إلى الحقل الموالي في الاستمارة، أو غير ذلك من الوسائل الممكنة الأخرى.
يُقصد بفقدان التركيز عموما أنّ "المُعطيات قد أُدخلت"، و يمكننا إذًا إجراء الشيفرة التي تعمل على تفحّصها أو حفظها في الخادم وماشابه ذلك.
الأحداث focus/blur
يُستدعى الحدث focus
عند التركيز على العنصر، ويُستدعى blur
عندما يفقد العنصر التركيز. لنستخدم هذه الأحداث لغرض التحقّق من القيم المُدخَلة في حقول المدخلات. إليك المثال التالي:
-
يتحقّق معالج
blur
من أنّ الحقل قد أُدخل فيه بريد الكترونيّ، فإذا لم يكن قد أُدخل فيه بريد الكترونيّ، يظهر رسالة خطأ. -
يُخفي معالج
focus
رسالة الخطأ (ليجري التحقّق عندblur
مرّة أخرى).
<style> .invalid { border-color: red; } #error { color: red } </style> Your email please: <input type="email" id="input"> <div id="error"></div> <script> input.onblur = function() { if (!input.value.includes('@')) { // ليس بريدا الكترونيّا input.classList.add('invalid'); error.innerHTML = 'Please enter a correct email.' } }; input.onfocus = function() { if (this.classList.contains('invalid')) { // أزل إشارة “الخطأ”، لأنّ المستخدم يريد إعادة إدخال شيء ما this.classList.remove('invalid'); error.innerHTML = ""; } }; </script>
تُمكّننا لغة HTML الحديثة من إجراء عدّة تحقّقات بواسطة سمات العناصر input مثل required
وpattern
وغيرها. وهي أحيانا كلّ ما نحتاجه. يُمكن أن تُستخدم جافاسكربت إذا أردنا المزيد من المرونة. ويمكننا أيضا إرسال القيمة المُعدّلة إلى الخادم تلقائيّا إذا كانت صحيحة.
التوابع focus/blur
تجعل التوابعُ elem.focus()
وelem.blur()
العنصر يكتسب/يفقد التركيز. على سبيل المثال، يمكن أن نمنع الزائر من مغادرة حقل المُدخَل إذا كانت القيمة غير صحيحة:
<style> .error { background: red; } </style> Your email please: <input type="email" id="input"> <input type="text" style="width:220px" placeholder="make email invalid and try to focus here"> <script> input.onblur = function() { if (!this.value.includes('@')) { // ليس بريدا الكترونيّا // أظهر الخطأ this.classList.add("error"); // وأرجع التركيز كما كان... input.focus(); } else { this.classList.remove("error"); } }; </script>
يعمل ذلك على جميع المتصفّحات باستثناء فايرفكس (خطأ برمجيّ).
إذا أدخلنا شيئا ما في الحقل ثمّ حاولنا استعمال المفتاح Tab
أو النقر خارج الـ<input>
، فإنّ onblur
يعمل على استعادة التركيز.
يُرجى التنبّه أنّه لا يمكننا "منع فقدان التركيز" بواسطة استدعاء event.preventDefault()
عند onblur
، لأنّ onblur
يعمل بعد أن يفقد العنصر التركيز.
تنبيه: فقدان التركيز الناشئ من جافاسكربت
قد يحدث فقدان التركيز لعدّة أسباب. إحداها أن ينقر المستخدم على مكان آخر. لكن يمكن لجافاسكربت ذاتها أن تتسبّب في ذلك أيضا، على سبيل المثال:
-
يجلب
alert
التركيز إليه، وبذلك فهو يتسبّب في فقدان التركيز عن العنصر (الحدثblur
)، وعندما يُزالalert
فإن التركيز يرجع حيث كان (حدثfocus
). - إذا حُذف عنصر ما من DOM، فإنّ ذلك يتسبّب في فقدان التركيز أيضا. لكن إذا أُدرج ثانية، لا يرجع معه التركيز.
تتسبّب هذه الميزات أحيانا في سوء سلوك معالجات focus/blur
-- تشتغل دون الحاجة إليها.
أفضل توصيفة هي الحذر عند استعمال هذه الأحداث. إذا أردنا تتبّع فقدان التركيز الناشئ من قبل المستخدمين، فعلينا أن نتجنّب التسبّب فيه بأنفسنا.
تمكين التركيز على أيّ عنصر: tabindex
هناك العديد من العناصر التي لا تدعم التركيز افتراضيًّا. تختلف القائمة قليلا بين المتصفّحات، لكنّ هناك شيئا ثابتا: يكون دعم focus/blur
مضمونا بالنسبة للعناصر التي يمكن للمستخدم أن يتفاعل معها، مثل <button>
و<input>
و<select>
و<a>
وما إلى ذلك.
في المقابل، لا تقبل العناصر التي أوجدت بغرض التنسيق، مثل <div>
و<span>
و<table>
، التركيز افتراضيًّا. لا يعمل التابع elem.focus()
معها، ولا تقع عليها الأحداث focus/blur
أبدا.
يمكن تغيير ذلك بواسطة السمة tabindex
في HTML.
يصير أيّ عنصر قابلا للتركيز إذا كانت له السّمة tabindex
. تمثّل قيمة السمة رقم ترتيب العنصر بين العناصر عند استخدام المفتاح Tab
(أو شيء من هذا القبيل) للتنقّل بينها.
هذا يعني لو أنّ لدينا عنصرين، أحدهما له tabindex="1"
، والثاني له tabindex="2"
، فإنّ ضغط المفتاح Tab
في العنصر الأوّل ينقل التركيز إلى العنصر الثاني.
يكون ترتيب التنقّل على النحو التالي: العناصر التي لها tabindex
من 1 فصاعدا تأتي أوّلا (حسب ترتيب tabindex
)، ثمّ العناصر التي ليس لها tabindex
(أي عناصر <input>
العاديّة).
يُنتقل إلى العناصر التي لها نفس قيمة tabindex
حسب ترتيبها في الشيفرة المصدريّة للمستند (الترتيب الافتراضيّ).
وهناك قيمتان خاصّتان:
tabindex="0"
تجعل العنصر ضمن العناصر التي ليس لها tabindex
. بمعنى، عند التنقّل بين العناصر، تأتي العناصر التي لها tabindex=0
بعد العناصر التي لها tabindex ≥ 1
. تُستخدم عادة لجعل عنصر ما قابلا للتركيز، مع المحافظة على ترتيب التنقّل الافتراضيّ. لجعل العنصر جزءًا من الاستمارة ومضاهيا لـ <input>
. tabindex="-1"
تمكّن من التركيز برمجيّا فقط على العنصر. يتجاهل المفتاح Tab
مثل هذه العناصر، لكنّ التابع elem.focus()
يعمل معها.
على سبيل المثال، إليك القائمة التالية:
Click the first item and press Tab. Keep track of the order. Please note that many subsequent Tabs can move the focus out of the iframe in the example. <ul> <li tabindex="1">One</li> <li tabindex="0">Zero</li> <li tabindex="2">Two</li> <li tabindex="-1">Minus one</li> </ul> <style> li { cursor: pointer; } :focus { outline: 1px dashed green; } </style>
لو نقرنا على العنصر الأوّل ثم ضغطنا على Tab
، سيكون الترتيب هكذا: 1 - 2 - 0
. عادة، لا تدعم <li>
التركيز، لكنّ tabindex
يجعل ذلك ممكنا كلّيّا، بما في ذلك الأحداث وإمكانيّة التنسيق بواسطة focus:
.
ملاحظة: تعمل الخاصيّة elem.tabIndex
أيضا
يمكننا إضافة tabindex
من خلال جافاسكربت باستخدام الخاصيّة elem.tabIndex
. يكون لذلك نفس الأثر.
التفويض: focusin/focusout
لا تنتشر الأحداث focus
وblur
نحو الأعلى. على سبيل المثال، لا يمكننا إسناد onfocus
إلى <form>
لإبرازه، هكذا:
<!-- عند التركيز على الاستمارة، أضف الصنف --> <form onfocus="this.className='focused'"> <input type="text" name="name" value="Name"> <input type="text" name="surname" value="Surname"> </form> <style> .focused { outline: 1px solid red; } </style>
لا يعمل المثال أعلاه، لأنّه عندما يركّز المستخدم على <input>
، فإنّ الحدث focus
يقع على ذلك المُدخَل فقط. لا ينتشر نحو الأعلى. وبذلك لا يشتغل form.onfocus
أبدا.
يوجد حلّان.
أوّلا، هناك ميزة تاريخيّة طريفة: لا تنتشر الأحداث focus/blur
نحو الأعلى، لكنّها تنتشر نحو الأسفل.
سيعمل هذا:
<form id="form"> <input type="text" name="name" value="Name"> <input type="text" name="surname" value="Surname"> </form> <style> .focused { outline: 1px solid red; } </style> <script> // (true ضع المعالج في مرحلة الانتشار نحو اﻷسفل (قيمة الوسيط اﻷخير form.addEventListener("focus", () => form.classList.add('focused'), true); form.addEventListener("blur", () => form.classList.remove('focused'), true); </script>
ثانيا، توجد هناك الأحداث focusin
وfocusout
التي هي مثل focus/blur
تماما غير أنّها تنتشر نحو الأعلى.
تنبّه أنّه يجب إسنادها باستخدام elem.addEventListener
، وليس on<event>
.
وبذلك هذا هو الحلّ البديل:
<form id="form"> <input type="text" name="name" value="Name"> <input type="text" name="surname" value="Surname"> </form> <style> .focused { outline: 1px solid red; } </style> <script> form.addEventListener("focusin", () => form.classList.add('focused')); form.addEventListener("focusout", () => form.classList.remove('focused')); </script>
الملخص
تقع الأحداث focus
وblur
على العنصر عندما يكتسب/يفقد التركيز.
خصائصها هي:
-
لا تنتشر نحو الأعلى. يمكن استخدام حالة الانتشار نحو الأسفل بدل ذلك أو
focusin/focusout
. -
لا تدعم معظم العناصر التركيز افتراضيًّا. استخدم
tabindex
لجعل أيّ شيء قابلا للتركيز.
يوجد العنصر المُركّز عليه حاليا في document.activeElement
.
التمارين
عنصر div قابل للتحرير
الأهميّة:5
أنشئ عنصر<div>
بحيث يتحوّل إلى مساحة نصّيّة <textarea>
عند النقر عليه.
تُمكّن المساحة النصّيّة من التعديل على شيفرة HTML التي بداخل العنصر <div>
.
عندما يضغط المستخدم على المفتاح Enter
أو عندما تفقد المساحة النصّيّة التركيز، فإنّها تعود <div>
، ويحلّ محتواها محلّ شيفرة HTML التي كانت بداخل <div>
.
كما هو مبيّن من هنا.
أنجز التمرين في البيئة التجريبية.
الحل
افتح الحلّ في البيئة التجريبية.
التحرير في خانة الجدول عند النقر عليها
الأهميّة: 5
اجعل خانات الجدول قابلة للتحرير عند النقر عليها.
- عند النقر، يجب أن تصير الخانة "قابلة للتحرير" (تظهر مساحة نصّيّة داخلها)، ويمكننا عندها التعديل على HTML. يجب أن لا يكون هناك أي تغيّر في الحجم، تبقى جميع الأبعاد على حالها.
- تظهر الأزرار OK وCANCEL تحت الخانة لإتمام/إلغاء التحرير.
- يمكن التحرير في خانة واحدة فقط في نفس الوقت. عندما تكون خانة ما في "وضع التحرير"، تُتجاهل النقرات على الخانات الأخرى.
- قد يكون في الجدول العديد من الخانات. استخدم تفويض الأحداث.
يمكن مشاهدة المثال من هنا.
أنجز التمرين في البيئة التجريبية
الحل
-
عند النقر، استبدل
innerHTML
الخاص بالخانة بـ<textarea>
بنفس الحجم وبدون إطار. يمكن استخدام جافاسكربت أو CSS لضبط الحجم الصحيح. -
اجعل
textarea.value
تساويtd.innerHTML
. - ركّز على المساحة النصّيّة.
- أظهر الأزرار OK/CANCEL تحت الخانة، وعالج النقر عليها.
افتح الحل في البيئة التجريبية.
التحكّم في الفأرة باستخدام لوحة المفاتيح
الأهميّة: 4
ركّز على الفأرة، ثمّ استخدم مفاتيح الأسهم لتحريكها.
كما في المثال من هنا.
ملاحظة: لا تضع معالجات الأحداث في غير العنصر #mouse
.
ملاحظة أخرى: لا تغيّر HTML/CSS. يجب أن تكون الطريقة عامّة وتعمل مع أيّ عنصر كا.
أنجز التمرين في البيئة التجريبية
الحل
يمكننا استخدام mouse.onclick
لمعالجة النقر، وجعل الفأرة "قابلة للتحريك" بواسطة position:fixed
، ثمّ استخدام mouse.onkeydown
لمعالجة مفاتيح الأسهم.
المزلق الوحيد هو أنّ keydown
يقع فقط على العناصر التي فيها التركيز. لذا نحتاج إلى إضافة tabindex
للعنصر. وبما أنّه يُمنع تغيير HTML، يمكننا استخدام الخاصيّة mouse.tabIndex
لذلك.
ملاحظة: يمكننا أيضا استبدال mouse.onclick
بـmouse.onfocus
.
ترجمة -وبتصرف- للمقال Focusing: focus/blur من سلسلة Browser: Document, Events, Interfaces لصاحبها Ilya Kantor
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.