إعادة عرض المحتوى معًا للمرة الأولى
مقدمة
على مدار ما يقرب من ثلاثين عامًا، كانت تجارب استخدام أجهزة الكمبيوتر المكتبي تركّز على لوحة المفاتيح والماوس أو لوحة اللمس كأداة إدخال رئيسية للمستخدم. ومع ذلك، خلال العقد الماضي، وفّرت الهواتف الذكية والأجهزة اللوحية نموذج تفاعل جديدًا: اللمس. مع طرح أجهزة Windows 8 المزوّدة بشاشة تعمل باللمس، والآن مع إصدار جهاز Chromebook Pixel الرائع المزوّد بشاشة تعمل باللمس، أصبحت شاشة اللمس جزءًا من تجربة سطح المكتب المتوقّعة. يتمثل أحد أكبر التحديات في إنشاء تجارب لا تعمل فقط على الأجهزة التي تعمل باللمس وأجهزة الماوس، بل أيضًا على هذه الأجهزة التي سيستخدم فيها المستخدم طريقتَي الإدخال معًا، أحيانًا في الوقت نفسه.
ستساعدك هذه المقالة في فهم كيفية دمج إمكانات اللمس في المتصفّح، وكيفية دمج آلية الواجهة الجديدة هذه في تطبيقاتك الحالية، وكيفية استخدام إمكانات اللمس مع إدخال الماوس.
حالة اللمس في منصة الويب
كان هاتف iPhone أول منصة رائجة تتضمّن واجهات برمجة تطبيقات مخصّصة للأجهزة التي تعمل باللمس ومضمّنة في متصفّح الويب. أنشأ العديد من مورّدي المتصفّحات الآخرين واجهات واجهة برمجة تطبيقات مشابهة تم إنشاؤها لتتوافق مع تنفيذ iOS، والذي يُوضّح الآن من خلال مواصفة"أحداث اللمس، الإصدار 1". تتوفّر أحداث اللمس في Chrome وFirefox على أجهزة الكمبيوتر المكتبي، وSafari على أجهزة iOS، وChrome ومتصفّح Android على أجهزة Android، بالإضافة إلى متصفّحات الأجهزة الجوّالة الأخرى، مثل متصفّح Blackberry.
كتب زميلي "بوريس سموس" دليلاً تعليميًا رائعًا على HTML5Rocks حول أحداث اللمس لا يزال يشكّل طريقة جيدة للبدء إذا لم يسبق لك الاطّلاع على أحداث اللمس. في الواقع، إذا لم يسبق لك التعامل مع أحداث اللمس، ننصحك بقراءة هذه المقالة الآن قبل المتابعة. يُرجى المتابعة، وسننتظرك.
هل اكتملت العملية؟ بعد أن حصلت على أساسيات عن أحداث اللمس، يكمن التحدي في كتابة التفاعلات المتوافقة مع اللمس في أنّ التفاعلات التي تتم من خلال اللمس يمكن أن تختلف كثيرًا عن أحداث الماوس (واللوحة والمسطرة اللتان تحاكيان الماوس)، وعلى الرغم من أنّ الواجهات التي تعمل باللمس تحاول عادةً محاكاة الماوس، إلا أنّ هذا المحاكاة ليس مثاليًا أو كاملاً. عليك العمل على كل من أسلوبَي التفاعل، وقد تحتاج إلى إتاحة كل واجهة بشكل مستقل.
الأهم من ذلك: قد يمتلك المستخدم جهاز لمس وماوسًا.
أنشأ العديد من المطوّرين مواقع إلكترونية ترصد بشكلٍ ثابت ما إذا كانت البيئة تتيح أحداث اللمس، ثم تفترض أنّها تحتاج فقط إلى إتاحة أحداث اللمس (وليس أحداث الماوس). هذا الافتراض غير صحيح الآن، فمجرد توفّر أحداث اللمس لا يعني أنّ المستخدم يستخدم جهاز الإدخال باللمس هذا بشكل أساسي. تتيح الآن أجهزة مثل Chromebook Pixel وبعض أجهزة الكمبيوتر المحمول التي تعمل بنظام التشغيل Windows 8 استخدام كل من أسلوبَي الإدخال باللمس والماوس، وستتوفّر هذه الميزة في المزيد من الأجهزة في المستقبل القريب. على هذه الأجهزة، من الطبيعي أن يستخدم المستخدمون كلّ من الماوس وشاشة اللمس للتفاعل مع التطبيقات، لذا فإنّ "تتوفّر ميزة اللمس" لا تعني "لا تحتاج إلى ميزة الماوس". لا يمكنك التفكير في المشكلة على أنّها "عليّ كتابة أسلوبَين مختلفَين للتفاعل والتبديل بينهما"، بل عليك التفكير في كيفية عمل التفاعلَين معًا بشكل مستقل. على جهاز Chromebook Pixel، أستخدم لوحة اللمس بشكل متكرّر، ولكنني ألمس الشاشة أيضًا. في التطبيق أو الصفحة نفسها، أفعل ما أراه مناسبًا في الوقت الحالي. من ناحية أخرى، نادرًا ما يستخدم بعض مستخدمي أجهزة الكمبيوتر المحمول التي تعمل باللمس شاشة اللمس، لذا يجب ألا يؤدي توفُّر ميزة الإدخال باللمس إلى إيقاف إمكانية التحكّم باستخدام الماوس أو إعاقتها.
قد يكون من الصعب معرفة ما إذا كانت بيئة المتصفّح لدى المستخدم تتيح الإدخال باللمس أم لا. من المفترض أن يشير المتصفّح على جهاز الكمبيوتر المكتبي دائمًا إلى توفّر أحداث اللمس حتى يمكن ربط شاشة لمس في أي وقت (على سبيل المثال، إذا أصبحت شاشة لمس متاحة من خلال KVM). لهذه الأسباب جميعها، يجب ألا تحاول تطبيقاتك التبديل بين اللمس والماوس، بل يجب أن تتيح استخدام كليهما.
إتاحة استخدام الماوس واللمس معًا
#1 - النقر والضغط: الترتيب "الطبيعي" للأشياء
المشكلة الأولى هي أنّ الواجهات التي تعمل باللمس تحاول عادةً محاكاة نقرات الماوس، وذلك لأنّ الواجهات التي تعمل باللمس يجب أن تعمل على التطبيقات التي لم تتفاعل إلا مع أحداث الماوس من قبل. يمكنك استخدام هذا الإجراء كاختصار، لأنّه سيستمر تشغيل أحداث "النقر"، سواء نقر المستخدم باستخدام الماوس أو نقر إصبعه على الشاشة. ومع ذلك، هناك بعض المشاكل في هذا الاختصار.
أولاً، عليك الانتباه عند تصميم تفاعلات لمس أكثر تقدمًا: عندما يستخدم المستخدم الماوس، ستستجيب الشاشة من خلال حدث النقر، ولكن عندما يلمس المستخدم الشاشة، سيحدث كل من حدثَي اللمس والنقر. عند النقر مرة واحدة، يكون ترتيب الأحداث على النحو التالي:
- touchstart
- touchmove
- touchend
- تمرير الماوس
- mousemove
- mousedown
- mouseup
- نقرة
يعني ذلك بالطبع أنّه في حال معالجة أحداث اللمس، مثل touchstart، عليك التأكّد من عدم معالجة حدث mousedown و/أو click المقابلَين أيضًا. إذا كان بإمكانك إلغاء أحداث اللمس (استدعاء preventDefault() داخل معالِج الحدث)، لن يتم إنشاء أي أحداث للماوس لللمس. من أهم قواعد عناصر المعالجة باللمس ما يلي:
ومع ذلك، يمنع هذا الإجراء أيضًا سلوك المتصفح التلقائي الآخر (مثل الانتقال للأعلى أو للأسفل)، على الرغم من أنّك عادةً ما تتعامل مع حدث اللمس بالكامل في معالِجك، وستحتاج إلى إيقاف الإجراءات التلقائية. بشكل عام، عليك معالجة جميع أحداث اللمس وإلغاؤها، أو تجنُّب استخدام معالِج لهذا الحدث.
ثانيًا، عندما ينقر المستخدم على عنصر في صفحة ويب على جهاز جوّال، تواجه الصفحات التي لم يتم تصميمها للتفاعل على الأجهزة الجوّالة تأخيرًا لا يقل عن 300 ملي ثانية بين حدث touchstart ومعالجة أحداث الماوس (mousedown). يمكن إجراء ذلك باستخدام Chrome، ويمكنك تفعيل "محاكاة أحداث اللمس" في أدوات مطوّري برامج Chrome لمساعدتك في اختبار الواجهات التي تعمل باللمس على نظام غير مزوّد بشاشة تعمل باللمس.
يهدف هذا التأخير إلى منح المتصفّح الوقت الكافي لتحديد ما إذا كان المستخدم ينفّذ إيماءة أخرى، لا سيما التصغير أو التكبير من خلال النقر مرّتين. من الواضح أنّ هذا يمكن أن يشكّل مشكلة في الحالات التي تريد فيها الحصول على استجابة فورية عند لمس الشاشة. نحن نعمل على حلّ هذه المشكلة لمحاولة الحدّ من السيناريوهات التي يحدث فيها هذا التأخير تلقائيًا.
الطريقة الأولى والأسهل لتجنُّب هذا التأخير هي "إخبار" متصفّح الأجهزة الجوّالة بأنّ صفحتك لن تحتاج إلى تكبير/تصغير، ويمكن إجراء ذلك باستخدام إطار عرض ثابت، مثلاً عن طريق إدراج ما يلي في صفحتك:
<meta name="viewport" content="width=device-width,user-scalable=no">
لا يكون هذا الإجراء مناسبًا في بعض الحالات، إذ يؤدي إلى إيقاف ميزة التصغير/التكبير باستخدام إصبعين، والتي قد تكون مطلوبة لتسهيل الاستخدام، لذا يُرجى استخدامها بشكلٍ مقتصد إن أمكن (إذا أوقفت ميزة التصغير/التكبير التي يستخدمها المستخدم، قد تحتاج إلى توفير طريقة أخرى لزيادة سهولة قراءة النص في تطبيقك). بالإضافة إلى ذلك، لا ينطبق هذا التأخير على متصفّح Chrome على الأجهزة المتوافقة مع أجهزة الكمبيوتر المكتبي والتي تتيح اللمس، والمتصفّحات الأخرى على الأنظمة الأساسية للأجهزة الجوّالة عندما تحتوي الصفحة على إطارات عرض لا يمكن تغيير حجمها.
#2: لا يتم تشغيل أحداث Mousemove من خلال اللمس
من المهمّ الإشارة في هذه المرحلة إلى أنّ محاكاة أحداث الماوس في واجهة تعمل باللمس لا تمتد عادةً إلى محاكاة أحداث mousemove، لذا إذا أنشأت عنصر تحكّم جميلًا يستند إلى الماوس ويستخدم أحداث mousemove، من المحتمل ألا يعمل مع جهاز يعمل باللمس ما لم تُضِف معالِجات touchmove على وجه التحديد أيضًا.
عادةً ما تُنفِّذ المتصفّحات تلقائيًا التفاعل المناسب للتفاعلات باللمس على عناصر التحكّم في HTML، لذا، على سبيل المثال، لن تعمل عناصر التحكّم في النطاقات في HTML5 إلا عند استخدام التفاعلات باللمس. ومع ذلك، إذا نفّذت عناصر التحكّم الخاصة بك، من المرجّح ألا تعمل على التفاعلات من النوع "النقر والسحب". في الواقع، لا تتيح بعض المكتبات الشائعة الاستخدام (مثل jQueryUI) التفاعلات باللمس بهذه الطريقة (على الرغم من أنّ هناك العديد من الإصلاحات لمعالجة هذه المشكلة في jQueryUI). كانت هذه إحدى المشاكل الأولى التي واجهتها عند ترقية تطبيق Web Audio Playground للعمل باللمس، لأنّ أشرطة التمرير كانت مستندة إلى jQueryUI، لذا لم تعمل مع تفاعلات النقر والسحب. لقد غيّرت إلى عناصر التحكّم بنطاق HTML5، ونجحت في حلّ المشكلة. بدلاً من ذلك، كان بإمكاني ببساطة إضافة عناصر تحكّم في لمس الشاشة لتعديل أشرطة التمرير، ولكن هناك مشكلة واحدة في ذلك…
#3: Touchmove وMouseMove ليسا متماثلين
من الأخطاء التي لاحظنا أنّ بعض المطوّرين يقعون فيها هي أنّ معالجات touchmove وmousemove تستدعي مسارات الرموز البرمجية نفسها. يُعدّ سلوك هذين الحدثَين قريبًا جدًا، ولكنّه يختلف بشكل طفيف، وعلى وجه الخصوص، تستهدف أحداث اللمس دائمًا العنصر الذي بدأت اللمسة عليه، في حين تستهدف أحداث الماوس العنصر الذي يقع عليه مؤشر الماوس حاليًا. لهذا السبب، لدينا حدثَا mouseover وmouseout، ولكن لا يتوفّر حدثَا touchover وtouchout مقابلَين، بل يتوفّر حدث touchend فقط.
إنّ الطريقة الأكثر شيوعًا التي يمكن أن تؤدّي إلى حدوث ذلك هي إذا أزلت (أو نقلت) العنصر الذي بدأ المستخدم بلمسه. على سبيل المثال، تخيل منصّة عرض بعناصر متغيّرة للصور تتضمّن معالِج لمس على منصّة العرض بالكامل لتتوافق مع سلوك التمرير المخصّص. عندما تتغيّر الصور المتوفّرة، يمكنك إزالة بعض عناصر <img>
وإضافة عناصر أخرى. إذا بدأ المستخدم باللمس على إحدى هذه الصور ثم أزلت العنصر، سيتوقف معالِج الأحداث (الذي يقع على أحد العناصر السابقة لعنصر img) عن تلقّي أحداث اللمس (لأنّه يتم إرساله إلى هدف لم يعُد في الشجرة)، وسيبدو أنّ المستخدم يضع إصبعه في مكان واحد على الرغم من أنّه ربما حرّكه وأزاله في النهاية.
يمكنك بالطبع تجنُّب هذه المشكلة عن طريق تجنُّب إزالة العناصر التي تحتوي على معالِجات لمس (أو تحتوي على عناصر أصلية تحتوي على معالِجات لمس) عندما تكون لمسة نشطة. بدلاً من ذلك، ننصحك بالانتظار إلى أن تتلقّى حدث touchstart ثم إضافة معالجات touchmove/touchend/touchcancel إلى الهدف لحدث touchstart (وإزالتها عند انتهاء/إلغاء الحدث)، بدلاً من تسجيل معالجات touchend/touchmove الثابتة. بهذه الطريقة، سيستمر تلقّي أحداث اللمس حتى إذا تم نقل العنصر المستهدَف أو إزالته. يمكنك تجربة هذا الأمر قليلاً هنا - يمكنك النقر على المربّع الأحمر مع الضغط على مفتاح Escape لإزالته من واجهة DOM.
#4: النقر والتأرجح
فصلت استعارة مؤشر الماوس موضع المؤشر عن الاختيار النشط، ما سمح للمطوّرين باستخدام حالات التمرير لإخفاء المعلومات التي قد تكون ذات صلة بالمستخدمين وعرضها. ومع ذلك، لا ترصد معظم واجهات اللمس في الوقت الحالي إصبعًا "يحوم" فوق هدف معيّن، لذا لا يُنصح بتقديم معلومات مهمة من الناحية الدلالية (مثل تقديم نافذة منبثقة بعنوان "ما هو هذا العنصر؟") استنادًا إلى التمرير فوق العنصر، ما لم تقدّم أيضًا طريقة ملائمة لللمس للوصول إلى هذه المعلومات. يجب أن تكون حذرًا بشأن كيفية استخدامك لميزة التمرير فوق العنصر من أجل نقل المعلومات إلى المستخدمين.
من المثير للاهتمام أنّه يمكن أن يتم تنشيط الفئة الزائفة :hover في CSS من خلال الواجهات التي تعمل باللمس في بعض الحالات، فعند النقر على عنصر، يصبح :نشطًا عندما يكون الإصبع على الشاشة، ويحصل أيضًا على الحالة :hover. (في Internet Explorer، لا يكون التأثير :hover ساريًا إلا عندما يكون إصبع المستخدم على الشاشة، بينما تحافظ المتصفّحات الأخرى على التأثير :hover ساريًا إلى أن تتم النقرة التالية أو حركة الماوس التالية). هذه طريقة جيدة لتشغيل القوائم المنبثقة على الواجهات التي تعمل باللمس. ومن الآثار الجانبية لتنشيط عنصر ما أنّه يتم أيضًا تطبيق الحالة :hover. على سبيل المثال:
<style>
img ~ .content {
display:none;
}
img:hover ~ .content {
display:block;
}
</style>
<img src="/awesome.png">
<div class="content">This is an awesome picture of me</div>
بعد النقر على عنصر آخر، يتوقف العنصر عن أن يكون نشطًا، وتنتهي حالة التمرير بمؤشر الماوس، تمامًا كما لو كان المستخدم يستخدم مؤشر الماوس ونقله بعيدًا عن العنصر. يمكنك لف المحتوى في عنصر <a>
لجعله نقطة توقف للتنقّل باستخدام مفتاح التبويب أيضًا، وبهذه الطريقة يمكن للمستخدم إيقاف المعلومات الإضافية أو إظهارها من خلال تمرير مؤشر الماوس فوقها أو النقر عليها أو النقر عليها باستخدام اللمس أو الضغط على مفتاح، بدون الحاجة إلى JavaScript. لقد فوجئتُ بسرور عندما بدأتُ العمل على Web Audio Playground ليعمل بشكل جيد مع الواجهات التي تعمل باللمس، إذ كانت قوائمي المنبثقة تعمل بشكل جيد عند لمسها، لأنّني استخدمتُ هذا النوع من البنية.
تعمل الطريقة أعلاه بشكل جيد مع الواجهات المستندة إلى مؤشر الماوس، بالإضافة إلى الواجهات التي تعمل باللمس. يختلف ذلك عن استخدام سمات "العنوان" عند التمرير فوقها، والتي لن تظهر عند تفعيل العنصر:
<img src="/awesome.png" title="this doesn't show up in touch">
#5: دقة اللمس مقارنةً بدقة الماوس
على الرغم من أنّ أجهزة الماوس تختلف عن الواقع بشكلٍ مفاهيمي، تبيّن أنّها دقيقة للغاية، لأنّ نظام التشغيل الأساسي يتتبّع بشكل عام دقة البكسل المحدّدة للمؤشر. من ناحية أخرى، تبيّن لمطوّري التطبيقات المتوافقة مع الأجهزة الجوّالة أنّ لمسات الأصابع على الشاشة تعمل باللمس ليست دقيقة بقدر كبير، ويعود ذلك في الغالب إلى حجم سطح الإصبع عند ملامستها للشاشة (وجزئيًا لأنّ أصابعك تحجب الشاشة).
أجرى العديد من الأفراد والشركات أبحاثًا مكثفة حول المستخدمين حول كيفية تصميم التطبيقات والمواقع الإلكترونية التي تتيح التفاعل باللمس، كما تم تأليف العديد من الكتب حول هذا الموضوع. النصيحة الأساسية هي زيادة حجم أهداف اللمس من خلال زيادة الحشو، وتقليل احتمالية النقرات غير الصحيحة من خلال زيادة الهامش بين العناصر. (لا يتم تضمين الهوامش في عملية رصد النتائج لأحداث اللمس والنقر، ولكن يتم تضمين الحشو). كان أحد الإصلاحات الأساسية التي أجريتها على Web Audio Playground هو زيادة أحجام نقاط الربط لتسهيل لمسها بدقة.
وقد أدخل العديد من مورّدي المتصفّحات الذين يتعاملون مع الواجهات المستندة إلى اللمس منطقًا في المتصفّح للمساعدة في استهداف العنصر الصحيح عندما يلمس المستخدم الشاشة والحدّ من احتمالية حدوث نقرات غير صحيحة، على الرغم من أنّ هذا عادةً ما يؤدي إلى تصحيح أحداث النقر فقط وليس الأحداث المتعلّقة بالتنقّل (على الرغم من أنّه يبدو أنّ Internet Explorer يعدّل أحداث mousedown/mousemove/mouseup أيضًا).
#6: يجب إبقاء عناصر معالجة اللمس محصورة في نطاقها، وإلا ستؤدي إلى حدوث تقطُّع في عملية التمرير
من المهم أيضًا حصر معالجات اللمس بالعناصر التي تحتاج إليها فقط، لأنّ عناصر اللمس يمكن أن تستهلك معدل نقل بيانات مرتفعًا جدًا، لذا من المهم تجنُّب استخدام معالجات اللمس في عناصر الانتقال السريع (لأنّ معالجة البيانات قد تتداخل مع تحسينات المتصفّح لإجراء الانتقال السريع بدون انقطاع باستخدام اللمس - تحاول المتصفّحات الحديثة الانتقال السريع باستخدام سلسلة مهام وحدة معالجة الرسومات، ولكن هذا مستحيل إذا كان عليها التحقّق من JavaScript أولاً لمعرفة ما إذا كان التطبيق سيعالج كل حدث لمس). يمكنك الاطّلاع على مثال على هذا السلوك.
من الإرشادات التي يجب اتّباعها لتجنُّب هذه المشكلة التأكّد من أنّه إذا كنت تتعامل فقط مع أحداث اللمس في جزء صغير من واجهة المستخدم، يجب إرفاق معالِجات اللمس هناك فقط (وليس على <body>
الصفحة مثلاً). بعبارة أخرى، عليك حصر نطاق معالِجات اللمس قدر الإمكان.
#7: تقنية اللمس المتعدّد
يتمثل التحدي الأخير المثير للاهتمام في أنّه على الرغم من أنّنا نشير إليها باسم واجهة المستخدم "اللمس"، إلا أنّها توفّر بشكل عام واجهة مستخدم متعددة اللمس، أي أنّ واجهات برمجة التطبيقات توفّر أكثر من إدخال لمسة واحدة في المرة الواحدة. عند بدء إتاحة ميزة اللمس في تطبيقاتك، عليك مراعاة كيفية تأثير اللمسات المتعدّدة في تطبيقك.
إذا كنت تُنشئ تطبيقات تعتمد بشكل أساسي على الماوس، يعني ذلك أنّك معتاد على الإنشاء باستخدام نقطة مؤشر واحدة كحد أقصى، لأنّ الأنظمة لا تتوافق عادةً مع مؤشرات الماوس المتعدّدة. في العديد من التطبيقات، ستتمكّن من ربط أحداث اللمس بواجهة مؤشر واحدة فقط، ولكن معظم الأجهزة التي رأيناها لأجهزة الكمبيوتر المكتبي التي تتضمّن شاشة تعمل باللمس يمكنها معالجة إدخالَين متزامنين على الأقل، ويبدو أنّ معظم الأجهزة الجديدة تتيح 5 إدخالات متزامنة على الأقل. لتطوير لوحة مفاتيح بيانو على الشاشة، ستحتاج بالتأكيد إلى إتاحة إمكانية استخدام Inputs متعددة لللمس في الوقت نفسه.
لا تتضمّن واجهات برمجة تطبيقات W3C Touch APIs التي تم تنفيذها حاليًا واجهة برمجة تطبيقات لتحديد عدد نقاط الاتصال التي تتيحها الأجهزة، لذا عليك استخدام أفضل تقدير لعدد نقاط الاتصال التي سيحتاجها المستخدمون، أو بالطبع الانتباه إلى عدد نقاط الاتصال التي تظهر لك في الممارسة والتكيّف معها. على سبيل المثال، في تطبيق البيانو، إذا لم تظهر لك أبدًا أكثر من نقطتَي لمس، قد تحتاج إلى إضافة بعض عناصر واجهة المستخدم "للألحان". تحتوي PointerEvents API على واجهة برمجة تطبيقات لتحديد إمكانات الجهاز.
إجراء تعديلات طفيفة
نأمل أن تكون هذه المقالة قد قدّمت لك بعض الإرشادات حول التحديات الشائعة في تنفيذ تفاعلات اللمس إلى جانب تفاعلات الماوس. من المهم أكثر من أي نصيحة أخرى أن تختبر تطبيقك على الأجهزة الجوّالة والأجهزة اللوحية وأجهزة الكمبيوتر المكتبي المزوّدة بشاشة تعمل باللمس وأجهزة الكمبيوتر المكتبي المزوّدة بجهاز تحكم بالألعاب. إذا لم يكن لديك جهاز لمس وماوس، استخدِم ميزة محاكاة أحداث اللمس في Chrome لمساعدتك في اختبار السيناريوهات المختلفة.
من الممكن بل من السهل نسبيًا اتّباع هذه الإرشادات لإنشاء تجارب تفاعلية جذابة تعمل بشكل جيد مع الإدخال باللمس والإدخال باستخدام الماوس، وحتى مع كلا أسلوبَي التفاعل في الوقت نفسه.