UIDex

رفع الملفات

File Upload / Dropzone

<input type="file">

اسمه كمانمنطقة الإفلات، حقل رفع الملف، دروب زون، أبلود، سحب وإفلات الملفات، حقل المرفقات، dropzone، file picker، file input، upload field، drag and drop upload، attachment field

رفع الملفات هو الطريق الذي يسلّم به المستخدم ملفاً من جهازه إلى التطبيق، وأساسه في الويب عنصر واحد: <input type="file">. الضغط عليه يفتح نافذة اختيار الملفات التابعة لنظام التشغيل، وهي نافذة لا تملك عليها الصفحة أي سلطة: لا تُفتح إلا بإيماءة من المستخدم، ولا تكشف مسار الملف على القرص، ولا يصل الصفحة منها إلا كائن File. وثلاثة أشياء تُخلط به. منطقة الإفلات (dropzone) هدف سحب لا حقل، أي مستطيل يستمع لحدثَي dragover و drop، وهي طريق ثانٍ إلى نفس الملفات ولا تنوب عن الحقل، لأن الإفلات لا وجود له أصلاً على شاشة اللمس ولا في لوحة المفاتيح. وزرّ «اختر ملفاً» ليس عنصراً مستقلاً في الغالب، وإنما <label> مربوطة بالحقل تنقل إليه ضغطة المؤشّر، ويبقى التركيز والتفعيل بلوحة المفاتيح في الحقل نفسه لأن الـ <label> لا تدخل ترتيب Tab. أما الرافع الكامل (uploader) فطبقة فوق ذلك كله: طابور، وتقدّم لكل ملف، وإلغاء، وإعادة محاولة. والحقل نفسه لا يرفع شيئاً، إنما يختار فقط، وإرسال البايتات مسؤولية fetch أو XMLHttpRequest، إلا أن يكون الحقل داخل <form> يحمل enctype="multipart/form-data" فيتولّى المتصفّح الإرسال عند التسليم.

لو قلت عليه…

«المربّع اللي بترمي فيه الملف»«المستطيل المنقّط اللي مكتوب فيه اسحب الملفات هنا»«زرار ارفع صورة»«الحاجة اللي بتفتح مجلّدات الجهاز عشان تختار ملف»«اللستة اللي بتبان تحت وفيها اسم الملف وشريط بيتحمّل»

العيّنة الحيّة

تفاعل مع الديمو. كل الأجزاء حقيقية ومرقّمة.

اسحب الملفات وأفلتها هنا<input type="file">
PDF أو PNG · حتى ٥ ميجابايت للملف الواحد
تقرير-الربع-الثالث.pdfتم الرفع
invoice_2026.pdf٨٦٠ كيلوبايت
RTLالاسم اللاتيني معزول بـ bdi، وزرّ الإزالة عند النهاية المنطقية

تشريح العنصر: كل جزء واسمه

مرّر على أي سطر ليتحدّد مكانه في الديمو فوق.

برومبت البناء

ابنِ حقل رفع ملفات بمنطقة إفلات. أبقِ <input type="file"> موجوداً في الـ DOM دائماً وأخفِه بأسلوب sr-only (موضع مطلق، بكسل واحد، clip-path) لا بـ display:none، حتى يبقى في ترتيب Tab وفي شجرة الوصول، واربطه بـ <label for> تكتب نصّها بنفسك، ولا تضع <button> داخلها؛ وإن احتجت زرّاً فنادِ showPicker() من معالج ضغط مباشر. اجعل منطقة الإفلات تستدعي preventDefault() في dragover و drop، واحسب dragenter و dragleave بعدّاد عمق حتى لا تومض الحدود، وامنع الإفلات الافتراضي على window كي لا يترك المتصفّح الصفحة ويفتح الملف. اعرض سطر قيود يذكر accept والحدّ الأقصى للحجم، وعامل accept كمرشِّح لا كتحقّق: افحص النوع والحجم بعد الاختيار وبعد الإفلات، ثم افحصهما على الخادم. اعرض لكل ملف صفّاً فيه الاسم داخل <bdi> والحجم وشريط تقدّم مستقلّ بـ role="progressbar" و aria-valuenow، واحسب النسبة من xhr.upload.onprogress بعد قراءة lengthComputable. أعطِ زرّ الإزالة اسماً مقروءاً يذكر اسم الملف، وانقل التركيز بعد الحذف إلى الصفّ التالي أو إلى زرّ الاختيار، وأعد input.value إلى سلسلة فارغة بعد كل اختيار حتى يمكن إعادة اختيار الملف نفسه. استخدم الخصائص المنطقية في كل مكان: margin-inline و padding-inline و inset-inline-start و border-inline-start، ولا تكتب left ولا right ولا margin-left. واجعل امتلاء الشريط بتغيير inline-size على عنصر مثبّت بـ inset-inline-start: 0 لا بـ scaleX، ونسّق الحجم بـ Intl.NumberFormat بنظام ترقيم واحد للمنتج كله.

سلوكه في الاتجاه من اليمين لليسار

الجزء ده حصري عندنا.

صفّ الملف

ينعكس

ترتيب الصفّ منطقي بالكامل: الأيقونة والاسم عند البداية، ثم الحجم وزرّ الإزالة عند النهاية، فينقلب الصفّ كله في العربية ويصير زرّ الإزالة عند اليسار. نفّذه بصفّ flex مع margin-inline-start: auto على الزرّ، ولا تكتب margin-left: auto ولا float: right. وإن رسمت حافّة ملوّنة تدلّ على حالة الملف فاجعلها border-inline-start لا border-left، وإلا وقفت الحافّة في الطرف المخالف للاسم بعد الانعكاس.

شريط تقدّم كل ملف

ينعكس

الشريط يمتلئ من البداية المنطقية، أي من اليمين في العربية. نفّذ الامتلاء بعنصر داخلي مثبّت بـ inset-inline-start: 0 يتغيّر فيه inline-size من صفر إلى ١٠٠٪، فينعكس وحده لأن الحافّة التي رُسِي عليها هي التي تبدّلت. وابتعد عن transform: scaleX() لأن transform-origin لا تقبل أي كلمة منطقية، وعن linear-gradient(to right, …) لأن التدرّجات كذلك بلا كلمات منطقية. أما عنصر <progress> الأصلي فيأخذ اتجاهه من قيمة direction الموروثة، ومعنى ذلك أن dir="ltr" الذي تضعه على الصفّ لإصلاح اسم الملف يقلب معه اتجاه الامتلاء.

اسم ملف لاتيني في صفّ عربي

لا ينعكس

اسم الملف نصّ يأتي من المستخدم ولا تعرف اتجاهه سلفاً، فالعلاج عزله لا تثبيت اتجاهه: <bdi> أو unicode-bidi: plaintext على العنصر الحامل له. ولا وجود لقيمة direction: auto في CSS، فالخاصية لا تقبل غير ltr و rtl، والسلوك التلقائي مكانه سمة dir="auto" أو كلمة plaintext التي تعزل وتأخذ الاتجاه من أول محرف قويّ. وبدون عزل تلتحق المحارف المحايدة الملاصقة للأرقام (الشرطة والشرطة السفلية والنقطة) باتجاه الفقرة العربية، فينقسم الاسم إلى جزر يُعاد ترتيبها من اليمين، ويظهر invoice_2026.pdf على الشاشة هكذا: pdf.2026_invoice. والقوسان يزيدان الأمر سوءاً لأن قيمة Bidi_Mirrored لهما Yes فينقلب شكلهما. ولا تعالج ذلك بـ dir="ltr" على الصفّ، فهو يقلب التخطيط والشريط معاً ويسيء إلى الأسماء العربية التي تحمل أرقاماً مثل «تقرير 2026.pdf».

الحجم ونظام الأرقام

لا ينعكس

حجم الملف رقم ووحدة، وهما لا ينعكسان، فالأرقام تُقرأ من اليسار إلى اليمين في العربية أيضاً. المشكلة في الخلط: صفحة تطبع ٢٫٤ بالأرقام الهندية ثم تكتب MB بحروف لاتينية، أو تبني السلسلة بـ toFixed(1) ثم تلصق بها وحدة عربية، فيقف «2.4 ميجابايت» في القائمة إلى جوار «٨٦٠ كيلوبايت». اختر نظام ترقيم واحداً للمنتج كله ونسّق من خلاله: Intl.NumberFormat("ar-EG", { style: "unit", unit: "megabyte", unitDisplay: "short" }) يعطيك الرقم والفاصل والوحدة من مصدر واحد، والفاصل العشري العربي هو U+066B لا النقطة. ولو أردت أرقاماً لاتينية داخل صفحة عربية فاطلبها صراحة بـ ar-EG-u-nu-latn.

نافذة النظام

لا ينعكس

نافذة اختيار الملفات ليست جزءاً من صفحتك: يرسمها نظام التشغيل، ويتبع اتجاهها ولغتها إعدادات الجهاز لا سمة dir ولا لغة واجهتك. فلا تَعِد المستخدم بنافذة عربية ولا تحاول تنسيقها. وكذلك النصّ الافتراضي داخل الحقل نفسه (Choose File و No file chosen) يأتي من لغة المتصفّح ولا يُترجَم ولا يُستبدَل، و::file-selector-button ينسّق الزرّ ولا يمسّ كلماته. وهذا وحده سبب كافٍ لإخفاء الحقل خلف <label> تكتب نصّها بنفسك بالعربية.

أسماؤه في الكود

كل سطر هو كلمة مكتبة واحدة عن نفس الشيء. خد السطر اللي بيكلّم مشروعك.

HTML<input type="file" accept multiple>الميزة كلها في وسم واحد؛ كل ما عداه زخرفة حوله.
HTMLinput.showPicker()يفتح نافذة النظام من الكود، ويرمي NotAllowedError بلا تفعيل من المستخدم.
HTMLevent.dataTransfer.filesمن هنا تصل الملفات المُفلتة، وسمة accept لا تُرشّح هذه القائمة إطلاقاً.
CSS::file-selector-buttonينسّق الزرّ الأصلي داخل الحقل، لكن نصّه يأتي من لغة المتصفّح ولا يتغيّر.
ARIArole="progressbar" + aria-valuenowلازم لكل شريط مرسوم بعناصر div بدل <progress>، وإلا فالنسبة غير مقروءة.
Ant Design<Upload.Dragger>يعطيك منطقة السحب وقائمة الملفات وحالة كل ملف جاهزة في مكوّن واحد.
Material UI<Button component="label"> + VisuallyHiddenInputالنمط الموثّق عندهم: زرّ منسّق يلفّ حقلاً مخفياً بالقصّ يظلّ قابلاً للتركيز.

شوف كمان

آخر تحديث · 2026-08-19