الحقل date

من موسوعة حسوب
< HTML‏ | input
مراجعة 03:31، 26 فبراير 2018 بواسطة عبد اللطيف ايمش (نقاش | مساهمات) (تعديل الأمثلة)

عناصر <input> ذات النوع date تُنشِئ حقل إدخال يسمح بانتقاء التاريخ بسهولة، والتاريخ يتضمن السنة والشهر واليوم لكن ليس الوقت time.

شكل هذا الحقل يختلف من متصفح إلى متصفح، فالدعم الحالي ليس مثاليًا (راجع قسم دعم المتصفحات لمزيدٍ من المعلومات)، لكن هذا الحقل سيُعرَض كحقل نصي في المتصفحات التي لا تدعمه:

<input type="date" name="date">

سيبدو هذا الحقل كما في الصورة الآتية في متصفحَي Chrome و Opera:

لقطة لكيفية عرض الحقل date في متصفحَي Chrome و Opera.
لقطة لكيفية عرض الحقل date في متصفحَي Chrome و Opera.

أما في متصفح Edge فسيبدو الحقل كما يلي:

لقطة لكيفية عرض الحقل date في متصفح Edge.
لقطة لكيفية عرض الحقل date في متصفح Edge.

الخاصية value

تحتوي الخاصية value على سلسلة نصية (DOMString)، التي تُمثِّل قيمة التاريخ المُدخَل في هذا الحقل، ويمكن وضع قيمة افتراضية للتاريخ داخل حقل date باستخدام الخاصية value، كما في المثال الآتي:

<input id="date" type="date" value="2017-06-01">

يجدر بالذكر أنَّ التاريخ المعروض قد يختلف عن قيمة الخاصية value، فصيغة التاريخ المعروض في الحقل تعتمد على محلية (locale) نظام تشغيل المستخدم، بينما قيمة القيمة value تكون على الشكل yyyy-mm-dd دومًا. يمكنك أيضًا الحصول على قيمة الخاصية value وضبطها عبر JavaScript باستخدام الخاصية HTMLInputElement.value، مثال:

var dateControl = document.querySelector('input[type="date"]');
dateControl.value = '2017-06-01';

استخدام حقول اختيار التاريخ

قد تظن من الوهلة الأولى أنَّ استخدام حقل اختيار التاريخ هو أمرٌ بسيط، فتلك الحقول توفِّر واجهة مستخدم سهلة الاستخدام لاختيار التواريخ، وتساهم في توحيد المدخلات التي تُرسَل إلى الخادوم، بعض النظر عن محلية المستخدم. لكن هنالك مشاكل مع الحقل date بسبب قلّة دعم المتصفحات له.

سنلقي نظرةً بادئ الأمر على أمثلة بسيطة ثم أمثلة معقدة عن الحقل date، ثم سنُقدِّم نصيحة عن كيفية التعامل مع المتصفحات غير الداعمة لهذا الحقل.

الاستخدام الأساسي لحقل اختيار التاريخ

أبسط استخدام للحقل date يتضمن عنصر <input> وعنصر <label> كما هو موضَّح في المثال الآتي:

<form>
  <div>
    <label for="bday">أدخل تاريخ ولادتك:</label>
    <input type="date" id="bday" name="bday">
  </div>
</form>

ضبط أقل وأكبر تاريخ يمكن اختياره

يمكننا استخدام الخاصيتين min و max لوضع حدود للتواريخ التي يستطيع المستخدم اختيارها، ففي المثال الآتي سنضبط أدنى تاريخ إلى 2018‎-09-‎01 وأقصى تاريخ إلى 2018‎-09-30:

<form>
  <div>
    <label for="travel">اختر موعد رحلتك:</label>
    <input type="date" id="travel" name="travel" min="2018-09-01" max="2018-09-30">
  </div>
</form>

لاحظ أنَّ بإمكانك اختيار الأيام الموجودة في شهر أيلول/سبتمبر فقط، إذ إنَّ قسم «الأيام» في الحقل سيكون قابلًا للتعديل فقط، ولا يمكن التمرير إلى أشهر أخرى عدا شهر أيلول/سبتمبر.

ملاحظة: يفترض أنَّ بإمكاننا استخدام الخاصية step لتحديد الفاصل بين الأيام التي يُسمَح باختيارها (فربما تريد السماح للمستخدم باختيار أيام الجمعة فقط)، لكن هذا لا يعمل بكفاءة في أيّ متصفح في وقت كتابة هذه الصفحة.

التحكم بحجم الحقل

لا يدعم الحقل date خاصيات التحكم بالحجم مثل size، لذا عليك استخدام CSS لهذا الغرض.

التحقق من الحقل

افتراضيًا، لا يوفِّر الحقل date أيّ نوع من أنواع التحقق من المدخلات، فالواجهة الرسومية التي توفرها المتصفحات لا تسمح عادةً للمستخدم بإدخال أيّ شيء عدا التواريخ، لكن يمكن ألّا يختار المستخدم قيمةً لهذا الحقل من الأساس.

إذا كنتَ تستخدم الخاصيتين min و max لوضع حدود للتواريخ المسموح بها (انظر قسم «ضبط أقل وأكبر تاريخ يمكن اختياره» أعلاه) فستُظهِر المتصفحات التي تدعم الحقل date رسالة خطأ عندما يكون التاريخ المُختار خارج هذه الحدود.

أضف إلى ذلك أنَّ بإمكانك استخدام الخاصية required لجعل هذا الحقل إجباريًا، وستُعرَض رسالة خطأ إذا حاول المستخدم إرسال النموذج دون تحديد قيمة لهذا الحقل.

ضبطنا في المثال الآتي أقل وأكبر تاريخ يمكن للمستخدم اختياره إضافةً إلى جعل الحقل إجباريًا:

<form>
  <div>
    <label for="travel">اختر موعد رحلتك (من بداية شهر أيلول إلى العشرين منه):</label>
    <input type="date" id="travel" name="travel" min="2018-09-01" max="2018-09-20" required>
  </div>
  <div>
    <input type="submit">
  </div>
</form>

إذا حاولت إرسال النموذج دون إكمال التاريخ (أو اخترتَ تاريخًا خارج الحدود المسموحة) فسيعرض المتصفح رسالة خطأ.

تحذير مهم: التحقق من مدخلات المستخدم عبر HTML ليس بديلًا عن التأكّد منها من جهة الخادوم، فمن السهل جدًا أن يُعدِّل أحدهم شيفرة HTML لكي يتجاوز آلية التحقق، ومن الممكن أن يتجاوز أحدهم نموذج HTML كليًا ويرسل البيانات إلى الخادوم مباشرةً. فلو لم تكن الشيفرة المشغّلة على خادومك تتحقق من المدخلات التي تستقبلها فمن الممكن أن تحدث كارثة إذا أرسل أحدهم بيانات غير مُنظمة كما يجب (أو بيانات كبيرة أو من نوع خطأ وهلم جرًّا).

التعامل مع دعم المتصفحات

وكما ذكرنا سابقًا، المشكلة الرئيسية مع حقول التاريخ في هذا الوقت هي دعم المتصفحات، فمثلًا يبدو شكل منتقي التاريخ كما يلي في متصفح Firefox على هواتف أندرويد:

لقطة لكيفية عرض الحقل date في متصفح Firefox على هواتف أندرويد.
لقطة لكيفية عرض الحقل date في متصفح Firefox على هواتف أندرويد.

المتصفحات التي لا تدعم هذا الحقل ستحوِّله إلى حقل نصي، لكن هذا يُسبِّب مشاكل في موضوع الواجهة الرسومية (فالتحكم بهذا الحقل سيكون مختلفًا) والتعامل مع البيانات.

المشكلة الثانية أشد وقعًا (كما ذكرنا سابقًا)، فقيمة الحقل date تكون موحّدة على الشكل yyyy-mm-dd أما محتوى الحقل النصي لا يُحدِّد صيغةً معيّنةً يجب أن يكون فيها، وهنالك الكثير من الطرائق التي يكتب فيها الناس التاريخ مثل:

  • ddmmyyyy
  • dd/mm/yyyy
  • mm/dd/yyyy
  • dd-mm-yyyy
  • mm-dd-yyyy

إحدى الطرائق لحل هذه الإشكالية هي استخدام الخاصية pattern على حقل date، وحتى لو لم يستعملها الحقل date المعروض في المتصفحات الحديثة، لكن الحقل النصي الذي سيظهر في المتصفحات غير الداعمة للحقل date سيستعملها.

جرِّب مثلًا عرض المثال الآتي في متصفح غير داعم للحقل date:

<form>
  <div>
    <label for="bday">أدخل تاريخ ولادتك:</label>
    <input type="date" id="bday" name="bday" required pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
  </div>
  <div>
    <input type="submit">
  </div>
</form>

إذا جربتَ إرسال النموذج فستلاحظ أنَّ المتصفح قد عرض رسالة خطأ إذا لم تُطابِق مدخلاتك النمط nnnn-nn-nn حيث يُمثِّل n رقمًا من 0 إلى 9. لكن هذا لا يمنع المستخدمين من إدخال تواريخ خطأ مثل التواريخ بالصيغة yyyy-dd-mm (لأننا نريد أن يكون التاريخ بالصيغة yyyy-mm-dd) أي ما زالت المشكلة قائمة.

أفضل طريقة للتعامل مع التواريخ في النماذج مع دعم جميع المتصفحات هي جعل المستخدم يُدخِل اليوم والشهر والتاريخ في حقول منفصلة (يستعمل العنصر <select> لهذا الغرض)، أو استخدام مكتبة JavaScript مثل jQuery date picker.

أمثلة

سنُنشِئ مجموعتين من عناصر النماذج لاختيار التاريخ، عنصر <input type="date"‎> ومجموعة من ثلاثة عناصر <select> لاختيار التاريخ في المتصفحات غير الداعمة للحقل data.

شيفرة HTML

تبدو شيفرة HTML كما يلي:

<form>
    <div class="nativeDatePicker">
      <label for="bday">أدخل تاريخ ولادتك:</label>
      <input type="date" id="bday" name="bday">
      <span class="validity"></span>
    </div>
    <p class="fallbackLabel">أدخل تاريخ ولادتك:</p>
    <div class="fallbackDatePicker">
      <span>
        <label for="day">اليوم:</label>
        <select id="day" name="day">
        </select>
      </span>
      <span>
        <label for="month">الشهر:</label>
        <select id="month" name="month">
          <option selected>January</option>
          <option>February</option>
          <option>March</option>
          <option>April</option>
          <option>May</option>
          <option>June</option>
          <option>July</option>
          <option>August</option>
          <option>September</option>
          <option>October</option>
          <option>November</option>
          <option>December</option>
        </select>
      </span>
      <span>
        <label for="year">السنة:</label>
        <select id="year" name="year">
        </select>
      </span>
    </div>
</form>

أسماء الأشهر ثابتة لأنها نفسها دومًا، لكن قيم اليوم والسنة تولّد ديناميكيًا اعتمادًا على الشهر والسنة المُختارَين (راجع التعليقات لتفهم كيف تعمل تلك الدوال).

جزء مهم من الشيفرة الذي عليك أن تنتبه له هو كيفية اكتشاف دعم المتصفح لحقل date، إذ أنشأنا عنصر <input> جديد وضبطنا قيمة الخاصية type إلى date ثم تأكدنا مباشرةً من قيمة الخاصية type فالمتصفحات غير الداعمة للحقل date ستضبط قيمة الخاصية type إلى text، وإذا كان الحقل date غير مدعوم فسنخفيه وسنعرض الحقول البديلة (<select>).

شيفرة JavaScript

سنُنشِئ أولّا بعض المتغيرات، بما فيها اللون الافتراضي، ثم سنضبط دالة لمعالجة الحدث load الذي يُطلَق بعد اكتمال تحميل الصفحة:

// تعريف المتغيرات
var nativePicker = document.querySelector('.nativeDatePicker');
var fallbackPicker = document.querySelector('.fallbackDatePicker');
var fallbackLabel = document.querySelector('.fallbackLabel');

var yearSelect = document.querySelector('#year');
var monthSelect = document.querySelector('#month');
var daySelect = document.querySelector('#day');

// إخفاء الحقول البديلة افتراضيًا
fallbackPicker.style.display = 'none';
fallbackLabel.style.display = 'none';

// اختيار إن كان المتصفح يدعم الحقل أم لا
var test = document.createElement('input');
test.type = 'date';
// إذا كان لا يدعمه فنفِّذ ما في الدالة الشرطية الآتية
if(test.type === 'text') {
  // إخفاء الحقل الأصلي وإظهار الحقل البديل
  nativePicker.style.display = 'none';
  fallbackPicker.style.display = 'block';
  fallbackLabel.style.display = 'block';

  // ملء قيم الحقول ديناميكيًا
  // (لكن الأشهر ستبقى نفسها)
  populateDays(monthSelect.value);
  populateYears();
}

function populateDays(month) {
  // حذف محتويات العنصر select
  // لكي تتم تهيئة العنصر للمجموعة الجديدة من القيم
  while(daySelect.firstChild){
    daySelect.removeChild(daySelect.firstChild);
  }

  // إنشاء متغير فيه عدد الأيام التي ستُضاف
  var dayNum;

  // 31 أو 30 يومًا؟
  if(month === 'January' | month === 'March' | month === 'May' | month === 'July' | month === 'August' | month === 'October' | month === 'December') {
    dayNum = 31;
  } else if(month === 'April' | month === 'June' | month === 'September' | month === 'November') {
    dayNum = 30;
  } else {
  // إذا كان الشهر هو شباط/فبراير فلنحسب إن كانت السنة كبيسة
    var year = yearSelect.value;
    (year - 2016) % 4 === 0 ? dayNum = 29 : dayNum = 28;
  }

  // إضافة الخيارات إلى عنصر select
  for(i = 1; i <= dayNum; i++) {
    var option = document.createElement('option');
    option.textContent = i;
    daySelect.appendChild(option);
  }

  // إذا تم اختيار اليوم فسنعيد ضبطه لكيلا يعود اليوم إلى 1
  // عند تغيير السنة
  if(previousDay) {
    daySelect.value = previousDay;

    // إذا كان اليوم مضبوطًا إلى 31 مثلًا في الشهر القديم
    // واختار المستخدم شهرًا فيه عدد أيام أقل مثل شباط/فبراير
    // فستحاول الشيفرة الآتية حل هذه الإشكالية
    if(daySelect.value === "") {
      daySelect.value = previousDay - 1;
    }

    if(daySelect.value === "") {
      daySelect.value = previousDay - 2;
    }

    if(daySelect.value === "") {
      daySelect.value = previousDay - 3;
    }
  }
}

function populateYears() {
  // الحصول على السنة الحالية
  var date = new Date();
  var year = date.getFullYear();

  // إضافة السنة الحالية و100 سنة قبلها إلى عنصر select
  for(var i = 0; i <= 100; i++) {
    var option = document.createElement('option');
    option.textContent = year-i;
    yearSelect.appendChild(option);
  }
}

yearSelect.onchange = function() {
  populateDays(monthSelect.value);
}

monthSelect.onchange = function() {
  populateDays(monthSelect.value);
}

var previousDay;

daySelect.onchange = function() {
  previousDay = daySelect.value;
}

دعم المتصفحات

Chrome Firefox Edge Safari Opera
20.0 57 12 غير مدعوم 10.62