١. مقدمة بسيطة 🚪
تخيل إنك قاعد قدام باب بيتك، وكل ما حد يعمل حاجة على الباب (يخبط، يفتح، يقفل) انت بتعرف وبترد على الحدث ده.
الـ keyboard events شبه كده بالظبط - هي طريقة الكمبيوتر إنه يعرف إيه اللي بيحصل على الكيبورد بتاعك.
٢. الأنواع الأساسية
keydown (لما تدوس)
// زي ما انت بتدوس على جرس الباب
input.addEventListener('keydown', function(event) {
console.log('حد دايس على الجرس دلوقتي');
// لو دوست على حرف A مثلاً
if (event.key === 'a') {
console.log('دوست على A');
}
});
keyup (لما ترفع صباعك)
// زي ما انت بتسيب جرس الباب
input.addEventListener('keyup', function(event) {
console.log('الجرس رجع لمكانه');
// مثال عملي: لو بتكتب رسالة
if (event.key === 'Enter') {
console.log('خلصت كتابة وضغطت إنتر');
}
});
keypress (deprecated)
element.addEventListener('keypress', function(event) {
console.log('تم الضغط والترك');
});
٣. الخصائص المهمة
const textInput = document.querySelector('#message-input');
textInput.addEventListener('keydown', function(event) {
// تخيل إنك سواق ميكروباص:
// معرفة أنهي زرار اتضغط (زي ما تعرف مين نده عليك في الشارع)
console.log('مين نده؟:', event.key);
// لو حد ضغط Shift (زي لما حد يقولك على جنب)
if (event.shiftKey) {
console.log('قف على جنب');
}
// لو حد ضغط Ctrl (زي إشارة المرور)
if (event.ctrlKey) {
console.log('إشارة حمرا، استنى');
}
// لو حد ضغط Alt (زي الزمور)
if (event.altKey) {
console.log('بيزمر: بييييب');
}
});
٤. مثال عملي من حياتنا: لعبة “عم جمال البقال كأنه جاتا ” 🏪
const gameInput = document.querySelector('#game-input');
let score = 0;
gameInput.addEventListener('keydown', function(event) {
// لو دوست على السهم اليمين
if (event.key === 'ArrowRight') {
console.log('عم جمال مشي يمين علشان يجيب الطماطم');
score += 5;
}
// لو دوست على السهم الشمال
if (event.key === 'ArrowLeft') {
console.log('عم جمال رجع للمحل تاني');
score += 3;
}
// لو دوست على مسافة
if (event.key === ' ') {
console.log('عم جمال قفز فوق القطة! 😺');
score += 10;
}
// اطبع النتيجة
console.log(`النتيجة دلوقتي: ${score} نقطة`);
});
٥. مثال للشات/الدردشة 💬
const chatInput = document.querySelector('#chat-input');
chatInput.addEventListener('keydown', function(event) {
// لو دوست إنتر
if (event.key === 'Enter') {
// منع السطر الجديد
event.preventDefault();
// لو مفيش رسالة، بلاش نعمل حاجة
if (chatInput.value.trim() === '') {
console.log('مينفعش نبعت رسالة فاضية يا كبير');
return;
}
// بعت الرسالة
console.log('جاري إرسال الرسالة: ' + chatInput.value);
chatInput.value = ''; // نضف مكان الكتابة
}
// لو عايز تمسح الرسالة كلها (Ctrl + Backspace)
if (event.ctrlKey && event.key === 'Backspace') {
console.log('مسحت الرسالة كلها مرة واحدة');
chatInput.value = '';
}
});
نصائح مهمة 💡
- دايماً اعمل
preventDefault()
لما تحتاج تمنع السلوك الديفولت للمتصفح - متنساش تمسح الـ event listeners لما متحتاجهمش
- اعمل حساب الـ mobile keyboards - مش كل الناس بتستخدم كيبورد حقيقي
- خلي بالك من الـ browser compatibility
إزاي تاخد بالك من كيبورد الموبايلات لما تعمل keyboard events
في الجافاسكريبت؟
المشاكل اللي ممكن تقابلك مع كيبورد الموبايل:
- الكيبورد اللمس مش بيعمل نفس الحركات زي الكيبورد لعادي:
- لو المستخدم اختار كلمة من الاقتراحات (Auto-Complete) ➡️ مبيبعتهاش كـ
key
events. - لو كتب بالـ Swipe/Glide ➡️ مبيحسبش كل حرف على حدة.
- لو عدل على الكلام بالـ Copy/Paste ➡️ مبيظهرش كـ
keydown
.
- لو المستخدم اختار كلمة من الاقتراحات (Auto-Complete) ➡️ مبيبعتهاش كـ
- الكود اللي مبنى على
keyup
/keydown
مش هيشتغل مع الموبايلات!
إيه البديل؟
input
event: ده البطل اللي بيشوف أي تغيير في الحقل بغض النظر عن مصدره:- ضغط أزرار
- اختيار اقتراح
- سحب أصبع (Swipe)
- تعديل بالنسخ/لصق
- حتى لو داس على زر المسح (Backspace)!
// بدل ما تكتب:
document.querySelector('input').addEventListener('keyup', function(e) { ... });
// اكتب:
document.querySelector('input').addEventListener('input', function(e) {
// هتشتغل مع اي تغيير في الحقل (حتى لو من الكيبورد اللمس)
});
ليه الحل ده كويس؟
- بيتعامل مع القيمة النهائية (
e.target.value
) مش طريقة الكتابة. - بيغطي كل طرق الإدخال الغريبة اللي ممكن تخطر على بالك!
أمثلة إضافية للتطبيق 🎯
١. عمل shortcut للسيف:
document.addEventListener('keydown', function(event) {
// Ctrl + S
if (event.ctrlKey && event.key === 's') {
event.preventDefault();
console.log('حفظت الملف يا معلم!');
}
});
٢. التحكم في الصوت:
document.addEventListener('keydown', function(event) {
switch(event.key) {
case 'ArrowUp':
console.log('علي الصوت شوية');
break;
case 'ArrowDown':
console.log('نزل الصوت شوية');
break;
case 'm':
console.log('اقفل الصوت خالص');
break;
}
});
٦. نصائح مهمة
- استخدم
event.preventDefault()
لو عايز تمنع السلوك الديفولت للمتصفح - اعمل تشيك على
event.repeat
لو عايز تعرف إذا كان المستخدم ضاغط على الزرار باستمرار - متنساش تعمل cleanup للـ event listeners لما متحتاجهمش
إزاي تكتشف إذا كان المستخدم ضاغط على الزرار باستمرار؟ (مع event.repeat
) ⌨️
المشكلة:
- لو المستخدم ضاغط على زرار معين وماسكه (زي السهم لأعلى في الألعاب)،
- الكيبورد العادي (الفيزيكي) بيبعت
keydown
event كل شوية لحد ما يرفع إصبعه. - عايزين نفرق بين:
- الضغطة الأولى ✅
- الضغط المستمر (اللي جه بعد كدا) ❌
الحل السحري: event.repeat
🔥
event.repeat
هي خاصية في الـkeyboard event
بترجعtrue
لو الحدث ده نتيجة ضغط مستمر على الزرار.
document.addEventListener('keydown', function(e) {
if (e.repeat) {
console.log('لا تعمل حاجة: الضغط مستمر!');
return; // وقف التنفيذ لو الضغط مستمر
}
console.log('ضغطة أولى: إعمل الحاجة اللي عايزها هنا!');
});
٧. مثال على عمل form validation
const input = document.querySelector('input');
input.addEventListener('keydown', function(event) {
// منع الأرقام في حقل النص
if (/^\d$/.test(event.key)) {
event.preventDefault();
console.log('مينفعش تكتب أرقام هنا');
}
});
input.addEventListener('keyup', function(event) {
// التحقق من طول النص
if (this.value.length < 3) {
console.log('لازم تكتب على الأقل 3 حروف');
}
});
٨. التعامل مع الـ modifier keys
إيه هي الـ modifier keys؟ وازاي تتعامل معاها في الجافاسكريبت؟ 🎛️
- دول مفاتيح خاصة مينفعش تستخدمهم لوحدهم، لكن بيعدلوا سلوك المفاتيح التانية لما تضغط عليهم مع بعض!
- مثال: لما تضغط
Ctrl + C
عشان تنسخ، الـCtrl
هو الـ modifier key.
أشهر الـ modifier keys:
Shift
⇧- بيخلي الحروف كابتل (A ➔ a)
- لو داس عليه مع حركة الماوس: بيعدل في الـ dragging.
Ctrl
(Control) 🎮- بيستخدم في الاختصارات زي
Ctrl + S
للحفظ.
- بيستخدم في الاختصارات زي
Alt
(Option في الماك) ⎇- بيستخدم في إظهار قوائم أو حاجات خاصة (زي
Alt + Tab
للتبويبات).
- بيستخدم في إظهار قوائم أو حاجات خاصة (زي
Meta
(مفتاح الـ Windows أو Command في الماك) ⊞- في الماك:
Cmd + C
للنسخ.
- في الماك:
document.addEventListener('keydown', function(event) {
let combination = [];
if (event.ctrlKey) combination.push('Ctrl');
if (event.altKey) combination.push('Alt');
if (event.shiftKey) combination.push('Shift');
combination.push(event.key);
console.log('الأزرار المضغوطة:', combination.join(' + '));
});
٩. أخطاء شائعة لازم تتجنبها
- متعتمدش على keyCode لأنها deprecated
- متنساش تعمل تشيك على القيم قبل ما تستخدمها
- خلي بالك من الـ browser compatibility
- استخدم event.key بدل event.which أو event.keyCode
١. “متعتمدش على keyCode
لأنها deprecated”
- اللي كان بيحصل:
قديمًا كنا نستخدمevent.keyCode
عشان نعرف رقم الزر المضغوط (مثلًا:13
لزر Enter). - المشكلة:
الخاصية دي اتقفلت (deprecated) في معايير الويب الحديثة، ومش مضمونة تشتغل في كل المتصفحات! - الحل:
استخدمevent.key
(يرجع اسم الزر كـ string مثل “Enter”) أوevent.code
(يرجع الكود الفيزيائي للزر مثل “KeyA”).
// ❌ خطأ:
if (event.keyCode === 13) { ... }
// ✅ صح:
if (event.key === 'Enter') { ... }
٢. “متنساش تعمل تشيك على القيم قبل ما تستخدمها”
اللي كان بيحصل: لو المستخدم دخل حاجة مش متوقعة (مثلًا: حروف في حقل أرقام)، التطبيق ممكن يقع!
الحل: دايماً افحص القيم قبل التعامل معها (Validation).
input.addEventListener('input', (e) => {
const value = e.target.value;
// مثال: تأكد إن القيمة رقم
if (isNaN(value)) {
alert('مينفعش تدخل حروف هنا يا معلم!');
return;
}
});
٣. “خلي بالك من الـ browser compatibility”
- اللي كان بيحصل: بعض الخصائص مش مدعومة في كل المتصفحات بنفس الشكل!
- مثال:
event.code
مش مدعوم في IE أبدًا. - مثال:
event.key
بترجع قيم مختلفة في Safari للمفاتيح الخاصة (مثل: ß vs ss).
- مثال:
- الحل:
- شوف MDN عشان تتأكد من الدعم.
- اختبر التطبيق على متصفحات مختلفة (Chrome, Firefox, Safari).
٤. “استخدم event.key بدل event.which أو event.keyCode”
- الفرق بينهم:
event.key
: بيرجع اسم الزر مفهوم للإنسان (مثل “ArrowUp”, “a”).event.whic
h وevent.keyCode
: بيرجعوا أرقام قديمة ومش دقيقة.
// ❌ مش هتعرف تفرق بين الأحرف الكبيرة والصغيرة:
if (event.keyCode === 65) { ... } // 65 for both 'a' and 'A'
// ✅ هتعرف تفرق بسهولة:
if (event.key === 'a') { ... } // Lowercase
if (event.key === 'A') { ... } // Uppercase (مع Shift)
١٠. مثال متقدم - عمل undo/redo
let history = [];
let currentIndex = -1;
document.addEventListener('keydown', function(event) {
// Ctrl + Z (Undo)
if (event.ctrlKey && event.key === 'z') {
event.preventDefault();
if (currentIndex > 0) {
currentIndex--;
applyChange(history[currentIndex]);
}
}
// Ctrl + Y (Redo)
if (event.ctrlKey && event.key === 'y') {
event.preventDefault();
if (currentIndex < history.length - 1) {
currentIndex++;
applyChange(history[currentIndex]);
}
}
});
function applyChange(change) {
console.log('تطبيق التغيير:', change);
}
١١. Event Propagation (Bubbling vs. Capturing) مع keyboard events 🎮
الفكرة الأساسية:
لما بتعمل حدث (زي ضغط زر في الكيبورد)، بيتحرك الحدث في 3 مراحل:
- Capturing Phase (من الأعلى للأسفل): من الـ
window
حتى العنصر الهدف. - Target Phase: عند العنصر الهدف نفسه.
- Bubbling Phase (من الأسفل للأعلى): من العنصر الهدف عائدة للـ
window
.
Event Bubbling vs. Capturing
1. الـ Bubbling (الانتشار من الداخل للخارج):
- كيف يعمل:
الحدث يبدأ من العنصر الهدف (مثل<input>
) ويرتفع للأعلى نحو العناصر الأب (مثل<div>
ثم<body>
). - مثال عملي:
<div id="parent"> <input type="text" id="child"> </div> <script> const parent = document.getElementById('parent'); const child = document.getElementById('child'); // Bubbling (القيمة الافتراضية) child.addEventListener('keydown', () => console.log('Child Bubbling')); parent.addEventListener('keydown', () => console.log('Parent Bubbling')); </script>
- النتيجة عند الضغط على زر في الـ input:
Child Bubbling → Parent Bubbling
2. الـ Capturing (الانتشار من الخارج للداخل):
- كيف يعمل:
الحدث يبدأ من العنصر الأب (مثل<body>
) وينزل للأسفل نحو العنصر الهدف (مثل<div>
ثم<input>
). - مثال عملي:
<div id="parent"> <input type="text" id="child"> </div> <script> const parent = document.getElementById('parent'); const child = document.getElementById('child'); // true لتفعيل Capturing child.addEventListener('keydown', () => console.log('Child Capturing'), true); parent.addEventListener('keydown', () => console.log('Parent Capturing'), true); // true لتفعيل Capturing </script>
- النتيجة عند الضغط على زر في الـ input:
Parent Capturing → Child Capturing
ترتيب التنفيذ الكامل للأحداث (Bubbling + Capturing) 🔄
// إضافة كل الأحداث معًا
parent.addEventListener('keydown', () => console.log('Parent Capturing'), true);
child.addEventListener('keydown', () => console.log('Child Capturing'), true);
child.addEventListener('keydown', () => console.log('Child Bubbling'));
parent.addEventListener('keydown', () => console.log('Parent Bubbling'));
- النتيجة عند الضغط على زر في الـ input:
Parent Capturing → Child Capturing → Child Bubbling → Parent Bubbling
إيقاف الانتشار بـ stopPropagation() 🛑
- يمكنك إيقاف الانتشار باستخدام
event.stopPropagation()
داخل الـfunc التي تتبع الحدث.
child.addEventListener('keydown', (e) => {
e.stopPropagation();
console.log('Event Stopped!');
});
- النتيجة الجديدة:
Parent Capturing → Child Capturing → Event Stopped!
- ملاحظة —> تم إيقاف الانتشار بعد مرحلة الـ Target، فلم يصل الحدث لمرحلة الـ Bubbling!
حالات عملية لاستخدام Bubbling و Capturing 🛠️
1. مثال على Capturing:
إغلاق dropdown عند الضغط خارجَه.
document.addEventListener('click', (e) => {
if (!dropdown.contains(e.target)) {
dropdown.style.display = 'none';
}
}, true); // Capturing لتأكيد التنفيذ قبل Bubbling
2. مثال على Bubbling:
تنفيذ حدث على زر داخل جدول.
table.addEventListener('keydown', (e) => {
if (e.target.tagName === 'BUTTON') {
console.log('Button pressed!');
}
}); // Bubbling (القيمة الافتراضية)
١٢. شرح كشف تتابع المفاتيح (Key Sequences)
الفكرة العامة 🧠
الكود ده بيسمع لضغطات الكيبورد، وبيتحقق إذا كان المستخدم داس تسلسل معين من المفاتيح (مثلًا: ↑ ↑ ↓ ↓ ← → ← → B A
)، وبيستدعي وظيفة معينة لو التتابع صح.
دا مفيد جدًا لعمل:
- أكواد سرية في الألعاب (مثل شفرات لعبة gata )
- اختصارات كيبورد معقدة
- تفعيل ميزات خفية في التطبيق
التنفيذ الفعلي 💻
1. تعريف الكلاس KeySequenceDetector
class KeySequenceDetector {
constructor(sequence, callback) {
this.sequence = sequence; // التسلسل المطلوب (مثل ['a', 'b', 'c'])
this.callback = callback; // الوظيفة اللي هتتنفذ لو التتابع اتطابق
this.currentSequence = []; // التتابع الحالي اللي المستخدم بيدوسه
this.timeout = null; // مؤقت لمسح التتابع لو المستخدم وقف
document.addEventListener('keydown', this.handleKeyPress.bind(this));
}
}
sequence: التتابع اللي عايزين نتحقق منه (مصفوفة من أسماء المفاتيح).
callback: الوظيفة اللي هتشتغل لو التتابع اتطابق.
currentSequence: بيتخزن فيه المفاتيح اللي المستخدم داسها بالترتيب.
timeout: بيستخدم لإعادة ضبط التتابع لو المستخدم وقف عن الضغط لمدة ثانية.
2. تنفيذ الدالة handleKeyPress
handleKeyPress(event) {
clearTimeout(this.timeout); // امسح المؤقت القديم
this.currentSequence.push(event.key); // أضف المفتاح المضغوط للتتابع الحالي
// لو التتابع الحالي أطول من المطلوب، امسح أول عنصر
if (this.currentSequence.length > this.sequence.length) {
this.currentSequence.shift();
}
// لو التتابع الحالي مطابق للمطلوب، شغل الكallback وامسح التتابع
if (this.isSequenceMatch()) {
this.callback();
this.currentSequence = [];
}
// ضع مؤقت جديد لمسح التتابع بعد ثانية
this.timeout = setTimeout(() => {
this.currentSequence = [];
}, 1000);
}
كل ما يتم ضغط زر، بنضيفه لـ
currentSequence
.لو طول التتابع الحالي زاد عن المطلوب، بنشيل أول زر (عشان نحافظ على الطول المطلوب).
لو التتابع الحالي مطابق للمطلوب، بنشغل الـ callback ونمسح التتابع.
بنضع مؤقت جديد لمسح التتابع بعد ثانية، لو المستخدم وقف عن الضغط لمدة ثانية.
3. تنفيذ دالة الـ (isSequenceMatch)
isSequenceMatch() {
return this.sequence.every((key, index) =>
key === this.currentSequence[index]
);
}
- الدالة دي بتستخدم every عشان تتأكد أن كل عنصر في التتابع المطلوب (sequence) يطابق العنصر المقابل في التتابع الحالي (currentSequence).
4. استخدام الكلاس - مثال عملي: تفعيل كود Konami 🎮
const konami = new KeySequenceDetector(
['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'],
() => console.log('Konami Code Activated! 🚀')
);
١٣. معالجة الأحداث المتزامنة (Concurrent Events)
الفكرة العامة 🧠
الكود ده بيسمح لك بتسجيل اختصارات كيبورد مكونة من عدة مفاتيح مضغوطة معًا (مثل Ctrl + Shift + P
) وتنفيذ وظيفة معينة عند الضغط عليها.
دا مفيد لـ:
- اختصارات التطبيقات (مثل حفظ الملف بـ
Ctrl + S
) - تفعيل ميزات خفية بواسطة تركيبات معقدة
- التحكم في الألعاب باستخدام أكثر من زر في نفس الوقت
1. تعريف الكلاس KeyCombinationHandler
class KeyCombinationHandler {
constructor() {
this.pressedKeys = new Set(); // المفاتيح المضغوطة حالياً
this.combinations = new Map(); // التركيبات المسجلة مع callbacks
document.addEventListener('keydown', this.handleKeyDown.bind(this));
document.addEventListener('keyup', this.handleKeyUp.bind(this));
}
}
2. دالة معالجة الضغط (handleKeyDown)
handleKeyDown(event) {
this.pressedKeys.add(event.key); // أضف المفتاح المضغوط
this.checkCombinations(); // تحقق من وجود تركيبة مطابقة
}
3. دالة معالجة الرفع (handleKeyUp)
handleKeyUp(event) {
this.pressedKeys.delete(event.key); // أزل المفتاح المرفوع
}
عند رفع المفتاح، نزيله من pressedKeys.
4. تسجيل تركيبة جديدة (registerCombination)
registerCombination(keys, callback) {
const sortedKeys = keys.sort().join('+'); // رتب المفاتيح أبجدياً
this.combinations.set(sortedKeys, callback);
}
- مثال: [‘Control’, ‘Shift’, ‘P’] تصبح
Control+P+Shift
بعد الترتيب. - دا بيضمن إن الترتيب اللي تضغط فيه المفاتيح مش مهم!
5. التحقق من التركيبات (checkCombinations)
checkCombinations() {
const currentKeys = Array.from(this.pressedKeys).sort().join('+');
if (this.combinations.has(currentKeys)) {
this.combinations.get(currentKeys)(); // نفذ الcallback
}
}
بنرتب المفاتيح المضغوطة حالياً أبجدياً.
بنشوف إذا كانت موجودة في الـ combinations.
لو وجدت match، بنشغل الcallback.
مثال عملي: تفعيل اختصار طباعة 🖨️
const handler = new KeyCombinationHandler();
handler.registerCombination(['Control', 'Shift', 'p'], () => {
console.log('تم تفعيل وضع الطباعة!');
window.print(); // افتح نافذة الطباعة
});
- لما المستخدم يضغط
Ctrl + Shift + P
➔ هتفتح نافذة الطباعة.
١٤. تحسين الأداء (Performance Optimization)
Debouncing للـ Keyboard Events
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
const efficientKeyHandler = debounce((event) => {
console.log('تم معالجة الحدث:', event.key);
}, 150);
document.addEventListener('keydown', efficientKeyHandler);
١٥. معالجة أحداث IME (Input Method Editor)
ما هو IME؟ أو Composition
- أداة تتيح كتابة أحرف معقدة (كالصينية/اليابانية) بلوحة مفاتيح عربية/لاتينية.
- مثال: كتابة “nihao” لتحويلها إلى “你好” (مرحبا بالصينية).
كيف تعمل العملية خطوة بخطوة؟ 🔄
البداية (
compositionstart
)- المستخدم يفتح لوحة IME أو يبدأ كتابة مقطع صوتي (مثل Pinyin للصينية).
- مثال: الضغط على زر لتحويل لوحة المفاتيح إلى وضع الإدخال الصيني.
التحديثات (
compositionupdate
)- مع كل حرف يُكتب، يتم تحديث النص المؤقت (المرحلة الانتقالية).
- مثال: كتابة “ni” → “ن” ثم “hao” → “ه” في Pinyin يعطي تحديثات متتالية.
الانتهاء (
compositionend
)- المستخدم يختار الحرف النهائي من القائمة أو يضغط Enter.
- مثال: اختيار “你好” من القائمة بعد كتابة “nihao”.
لماذا تسمى “Composition”؟ 🧩
- لأنها عملية تأليف/تركيب الأحرف من مكونات أبسط:
- أصوات (Pinyin للصينية)
- مقاطع (Hiragana/katakana لليابانية)
- أشكال (Strokes للكورية)
مثال حي من واقع الحياة 🌏
الإدخال | المرحلة | الناتج |
---|---|---|
n | compositionstart | (بدء التركيب) |
ni | compositionupdate | 你 (مؤقت) |
nih | compositionupdate | 你ه (مؤقت) |
niha | compositionupdate | 你好 (مؤقت) |
nihao + Enter | compositionend | 你好 (نهائي) |
الفرق بين الـ Composition والكتابة العادية ⌨️
الكتابة العادية | Composition | |
---|---|---|
الأحداث | input , keydown | compositionstart/update/end |
السرعة | فورية | تحتاج تأكيد من المستخدم |
الاستخدام | اللغات اللاتينية | اللغات الآسيوية المعقدة |
الأحداث الأساسية:
1. حدث compositionstart
🟢
input.addEventListener('compositionstart', () => {
console.log('بدء تركيب النص');
});
متى يحدث؟
- عند بدء إدخال نص عبر IME (مثل فتح لوحة الرموز الصينية).
الاستخدام
- تهيئة حالة التطبيق لاستقبال إدخال معقد.
2. حدث compositionupdate
🟡
input.addEventListener('compositionupdate', (e) => {
console.log('تحديث:', e.data);
});
متى يحدث؟
- مع كل تغيير في النص المؤقت أثناء الكتابة عبر IME.
الاستخدام
- كتابة “k” ثم “a” ثم “n” في IME الياباني يعطي تحديثات متتالية.
3. حدث compositionend
🔴
input.addEventListener('compositionend', (e) => {
console.log('نص نهائي:', e.data);
});
متى يحدث؟
- عند انتهاء الإدخال (بالضغط Enter أو اختيار من القائمة).
الاستخدام
- معالجة النص النهائي (مثل حفظه أو إرساله).
١٦. Virtual Keyboard Handler
الهدف منه 🎯
هذا الكود بيسمح لك بعمل لوحة مفاتيح افتراضية بتغيير تخطيط الحروف حسب حالة الـ Shift.
مثلاً:
- في الوضع العادي (
default
): الضغط علىq
ينتجض
- مع الضغط على Shift (
shift
): الضغط علىq
ينتجَ
(حركة فتحة)
1. تعريف الكلاس وتهيئة المتغيرات
class VirtualKeyboardHandler {
constructor() {
this.layout = new Map(); // خزانة التخطيطات المختلفة
this.currentLayout = 'default'; // التخطيط الحالي
this.initializeLayout(); // تعبئة الخزانة بالتخطيطات
}
}
- layout: خريطة (Map) بتخزن كل تخطيطات الكيبورد (عادي - مع Shift - إلخ)
- currentLayout: التخطيط الحالي المستخدم
2. تهيئة التخطيطات (initializeLayout)
initializeLayout() {
// التخطيط العادي (بدون Shift)
this.layout.set('default', {
'q': 'ض', 'w': 'ص', 'e': 'ث', 'r': 'ق',
// ... باقي الحروف
});
// التخطيط مع Shift (للحركات أو الرموز)
this.layout.set('shift', {
'q': 'َ', 'w': 'ً', 'e': 'ُ', 'r': 'ٌ',
// ... باقي الرموز
});
}
- كل تخطيط عبارة عن كائن بيحول المفاتيح (مثل q) إلى قيمها (مثل ض)
- default: التخطيط الأساسي للكيبورد العربية
- shift: تخطيط الحركات (الفتحة، الضمة، إلخ)
3. معالجة الضغط على المفاتيح (handleKeyPress)
handleKeyPress(event) {
// تحديد التخطيط بناءً على Shift
const layoutMap = this.layout.get(event.shiftKey ? 'shift' : 'default');
// لو الزر موجود في التخطيط الحالي
if (layoutMap.hasOwnProperty(event.key)) {
event.preventDefault(); // امنع الكتابة العادية
return layoutMap[event.key]; // أرجع الحرف/الرمز المطلوب
}
return event.key; // لو الزر مش موجود، أرجع قيمته الأصلية
}
event.shiftKey
: بيتحقق إذا كان زر Shift مضغوطpreventDefault()
: بيوقف السلوك الافتراضي للزر (مثل كتابة q بالإنجليزية)return layoutMap[event.key]
: إرجاع القيمة المطابقة للمفتاح المضغوط
4. تنفيذ الكود
const keyboard = new VirtualKeyboardHandler();
// عند ضغط زر في الكيبورد
document.addEventListener('keydown', (e) => {
const output = keyboard.handleKeyPress(e);
console.log('الحرف الناتج:', output);
});
نصائح إضافية للأداء المتقدم
- استخدم
requestAnimationFrame
مع الـ keyboard events اللي بتحتاج تحديث مرئي - اعمل cleanup للـ event listeners باستخدام
AbortController
- استخدم
WeakMap
للتخزين المؤقت للبيانات المرتبطة بالعناصر - اعمل throttling للأحداث المتكررة زي الـ key repeat
- استخدم Web Workers للعمليات المعقدة