اذهب إلى المحتوى

التركيز على عناصر الاستمارات الإلكترونية والتنقل بينها في جافاسكربت


محمد أمين بوقرة

يمكن للعنصر أن يحصل على التركيز من المستخدم إمّا بالنقر عليه مباشرة أو باستعمال المفتاح 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 تحت الخانة لإتمام/إلغاء التحرير.
  • يمكن التحرير في خانة واحدة فقط في نفس الوقت. عندما تكون خانة ما في "وضع التحرير"، تُتجاهل النقرات على الخانات الأخرى.
  • قد يكون في الجدول العديد من الخانات. استخدم تفويض الأحداث.

يمكن مشاهدة المثال من هنا.

أنجز التمرين في البيئة التجريبية

الحل

  1. عند النقر، استبدل innerHTML الخاص بالخانة بـ <textarea> بنفس الحجم وبدون إطار. يمكن استخدام جافاسكربت أو CSS لضبط الحجم الصحيح.
  2. اجعل textarea.value تساوي td.innerHTML.
  3. ركّز على المساحة النصّيّة.
  4. أظهر الأزرار 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


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...