١٥ ديسمبر ٢٠٢١

الإحداثيات

لتحريك العناصر ، يجب أن نكون على دراية بالإحداثيات.

تتعامل معظم طرق JavaScript مع أحد نظامي الإحداثيات:

  1. ** نسبة إلى النافذة ** – تشبه “الموضع: ثابت” ، محسوبة من أعلى النافذة / الحافة اليسرى.     – سنشير إلى هذه الإحداثيات كـ “clientX / clientY” ، وسيصبح سبب هذا الاسم واضحًا لاحقًا ، عندما ندرس خصائص الحدث.
  2. ** بالنسبة إلى المستند ** – يشبه “الموضع: مطلق” في جذر المستند ، محسوبًا من أعلى المستند / الحافة اليسرى.     – سنشير لهم pageX / pageY.

عندما يتم تمرير الصفحة إلى البداية ، بحيث يكون الركن العلوي / الأيسر من النافذة هو بالضبط أعلى / يسار الزاوية ، فإن هذه الإحداثيات تساوي بعضها البعض. ولكن بعد تغيرات المستند ، تتغير الإحداثيات النسبية للعناصر ، حيث تتحرك العناصر عبر النافذة ، بينما تظل الإحداثيات النسبية للمستندات كما هي.

في هذه الصورة ، نأخذ نقطة في المستند ونعرض إحداثياتها قبل التمرير (يسار) وبعده (يمين):

عند تمرير المستند:

  • pageY – ظلت الإحداثيات النسبية للمستندات كما هي ، يتم حسابها من أعلى المستند (تم التمرير الآن).
  • `العميل '- تغيرت إحداثيات النافذة (أصبح السهم أقصر) ، حيث اقتربت نفس النقطة من أعلى النافذة.

إحداثيات العنصر: getBoundingClientRect

تُرجع الطريقة elem.getBoundingClientRect () إحداثيات النافذة لمستطيل صغير يشتمل على “elem” ككائن [DOMRect] مدمج (https://www.w3.org/TR/geometry-1/#domrect ) صف دراسي.

خصائص DOMRect الرئيسية:

  • x / y – إحداثيات X / Y لأصل المستطيل بالنسبة للنافذة ،
  • العرض / الارتفاع – عرض / ارتفاع المستطيل (يمكن أن يكون سالبًا).

بالإضافة إلى ذلك ، هناك خصائص مشتقة:

  • أعلى / أسفل – إحداثي Y لحافة المستطيل العلوي / السفلي ،
  • “يسار / يمين” – إحداثي X لحافة المستطيل الأيسر / الأيمن.

على سبيل المثال ، انقر فوق هذا الزر لرؤية إحداثيات نافذته:

إذا قمت بتمرير الصفحة وتكرارها ، فستلاحظ أنه مع تغير موضع الزر النسبي للنافذة ، تتغير إحداثيات نافذتها (y / top / bottom إذا قمت بالتمرير عموديًا) أيضًا.

إليكم صورة ناتج elem.getBoundingClientRect ():

كما ترى ، يصف x / y وwidth / height المستطيل بالكامل. يمكن حساب الخصائص المشتقة منها بسهولة:

  • left = x
  • top = y
  • right = x + width
  • bottom = y + height

يرجى الملاحظة:

  • قد تكون الإحداثيات كسور عشرية ، مثل 10.5. هذا طبيعي ، يستخدم المتصفح داخليًا الكسور في الحسابات. لا يتعين علينا تقريبها عند الضبط على style.left / top.
  • قد تكون الإحداثيات سلبية. على سبيل المثال ، إذا تم تمرير الصفحة بحيث يكون “elem” أعلى النافذة الآن ، فإن “elem.getBoundingClientRect (). top” هو سلبي.
رياضيا ، يتم تعريف المستطيل بشكل فريد بنقطة البداية `` (س ، ص) `وناقل الاتجاه` (العرض والارتفاع) `. لذا فإن الخصائص الإضافية المشتقة هي للسهولة في التعامل بها.

من الناحية الفنية ، من الممكن أن يكون "العرض / الارتفاع" سالبًا ، مما يسمح للمستطيل "الموجه" ، على سبيل المثال لتمثيل اختيار الماوس مع بداية ونهاية تم وضع علامة عليها بشكل صحيح.

تعني قيم "العرض / الارتفاع" السلبية أن المستطيل يبدأ من الزاوية السفلية اليمنى ثم "ينمو" إلى اليسار.

إليك مستطيل يحتوي على "عرض" و "ارتفاع" سالب (على سبيل المثال ، "عرض = -200" ، "ارتفاع = -100"):

![](coordinates-negative.svg)

كما ترى ، `اليسار / الأعلى` لا يساوي` x / y` في هذه الحالة.

من الناحية العملية ، يُرجع `elem.getBoundingClientRect ()` دائمًا العرض / الارتفاع الموجب ، وهنا نذكر `العرض / الارتفاع` السلبي فقط لكي تفهم لماذا هذه الخصائص التي تبدو مكررة ليست في الواقع مكررة.
Internet Explorer: no support for x/y

لذلك يمكننا إما إنشاء ملف متعدد (أضف حروفًا في DomRect.prototype) أو فقط استخدامأعلى / يسار ، لأنها دائمًا ما تكون مثل x / y لـعرض / ارتفاع إيجابي ، خاصة في نتيجة elem.getBoundingClientRect ().

هناك تشابه واضح بين الإحداثيات النسبية للنافذة و "الموضع: ثابت" في CSS.

ولكن في وضع CSS ، تعني خاصية `right` المسافة من الحافة اليمنى ، وتعني خاصية` bottom` المسافة من الحافة السفلية.

إذا نظرنا فقط إلى الصورة أعلاه ، يمكننا أن نرى أنه في JavaScript ليس كذلك. يتم حساب جميع إحداثيات النوافذ من الزاوية العلوية اليسرى ، بما في ذلك هذه الإحداثيات.

elementFromPoint (x، y)

يؤدي استدعاء "document.elementFromPoint (x، y)إلى إرجاع العنصر الأكثر تداخلاً في إحداثيات النافذة(x، y)`.

الصيغة هي:

let elem = document.elementFromPoint(x, y);

على سبيل المثال ، يبرز الرمز أدناه ويخرج علامة العنصر الموجود الآن في منتصف النافذة:

let centerX = document.documentElement.clientWidth / 2;
let centerY = document.documentElement.clientHeight / 2;

let elem = document.elementFromPoint(centerX, centerY);

elem.style.background = 'red';
alert(elem.tagName);

نظرًا لأنه يستخدم إحداثيات النافذة ، فقد يختلف العنصر اعتمادًا على موضع التمرير الحالي.

````عنوان تحذيري="بالنسبة إلى إحداثيات خارج النافذة ، يُرجعelementFromPoint القيمة الخاليةلا تعمل الطريقةdocument.elementFromPoint (x، y) إلا إذا كانت "(x، y)` داخل المنطقة المرئية.

إذا كانت أي من الإحداثيات سلبية أو تجاوزت عرض / ارتفاع النافذة ، فتُرجع null.

إليك خطأ نموذجي قد يحدث إذا لم نتحقق منه:

let elem = document.elementFromPoint(x, y);
// if the coordinates happen to be out of the window, then elem = null
elem.style.background = ''; // Error!
## استخدام الوضع "الثابت"

معظم الوقت نحتاج إحداثيات من أجل وضع شيء ما.

لإظهار شيء بالقرب من عنصر ، يمكننا استخدام `getBoundingClientRect` للحصول على إحداثياته ، ثم CSS` position` مع `left / top` (أو` right / bottom`).

على سبيل المثال ، تعرض الدالة `createMessageUnder (elem، html)` أدناه الرسالة تحت `elem`:

```js
let elem = document.getElementById("coords-show-mark");

function createMessageUnder(elem, html) {
  // create message element
  let message = document.createElement('div');
  // better to use a css class for the style here
  message.style.cssText = "position:fixed; color: red";

  // عند تعيين إحداثيات  ، لا تنسى "px"!
  let coords = elem.getBoundingClientRect();

  message.style.left = coords.left + "px";
  message.style.top = coords.bottom + "px";

  message.innerHTML = html;

  return message;
}

/// الاستخدام:
// أضفه لمدة 5 ثوانٍ في document
let message = createMessageUnder(elem, 'Hello, world!');
document.body.append(message);
setTimeout(() => message.remove(), 5000);
```

```online
Click the button to run it:

<button id="coords-show-mark">Button with id="coords-show-mark", the message will appear under it</button>
```

يمكن تعديل الكود لإظهار الرسالة على اليسار ، اليمين ، أدناه ، تطبيق الرسوم المتحركة CSS "تتلاشى فيه" وهلم جرا. هذا سهل ، لأن لدينا جميع إحداثيات وأحجام العنصر.

لكن لاحظ التفاصيل المهمة: عندما يتم تمرير الصفحة ، تتدفق الرسالة بعيدًا عن الزر.

والسبب واضح: يعتمد عنصر الرسالة على "الموضع: ثابت" ، لذلك يبقى في نفس مكان النافذة أثناء تمرير الصفحة بعيدًا.

لتغيير ذلك ، نحتاج إلى استخدام الإحداثيات المستندة إلى المستندات و "الموضع: مطلق".

## إحداثيات المستند [#getCoords]

تبدأ الإحداثيات المتعلقة بالمستند من الزاوية العلوية اليسرى من المستند ، وليس النافذة.

في CSS ، تتوافق إحداثيات النافذة مع "الموضع: ثابت" ، في حين أن إحداثيات المستند تشبه "الموضع: مطلق" في الأعلى.

يمكننا استخدام "الموضع: مطلق" و "أعلى / يسار" لوضع شيء ما في مكان معين من المستند ، بحيث يبقى هناك أثناء تمرير الصفحة. لكننا بحاجة إلى الإحداثيات الصحيحة أولاً.

لا توجد طريقة قياسية للحصول على إحداثيات المستند لعنصر. ولكن من السهل كتابتها.

يتم توصيل نظامي الإحداثيات بواسطة الصيغة:
- `pageY` =` clientY` + ارتفاع الجزء الرأسي القابل للتمرير من المستند.
- `pageX` =` clientX` + عرض الجزء الأفقي القابل للتمرير من المستند.

ستأخذ الدالة `getCoords (elem)` إحداثيات النافذة من `elem.getBoundingClientRect ()` وتضيف التمرير الحالي إليها:


```js
// get document coordinates of the element
function getCoords(elem) {
  let box = elem.getBoundingClientRect();

  return {
    top: box.top + window.pageYOffset,
    right: box.right + window.pageXOffset,
    bottom: box.bottom + window.pageYOffset,
    left: box.left + window.pageXOffset
  };
}
```

إذا استخدمناها في المثال أعلاه مع "الموضع: مطلق" ، فستظل الرسالة بالقرب من العنصر عند التمرير.

وظيفة `createMessageUnder` المعدلة:

```js
function createMessageUnder(elem, html) {
  let message = document.createElement('div');
  message.style.cssText = "position:absolute; color: red";

  let coords = getCoords(elem);

  message.style.left = coords.left + "px";
  message.style.top = coords.bottom + "px";

  message.innerHTML = html;

  return message;
}
```

## ملخص

تحتوي أي نقطة في الصفحة على إحداثيات:

1. بالنسبة إلى النافذة - `elem.getBoundingClientRect ()`.
2. بالنسبة إلى المستند - `elem.getBoundingClientRect ()` بالإضافة إلى تمرير الصفحة الحالية.

إحداثيات النوافذ رائعة للاستخدام مع "الموضع: ثابت" ، وإحداثيات المستند جيدة مع "الموضع: مطلق".

كلا النظامين المنسقين لهما إيجابيات وسلبيات ؛ هناك أوقات نحتاج فيها إلى واحد أو الآخر ، تمامًا مثل CSS `position`` مطلق` و` ثابت`.

مهمه

في iframe أدناه ، يمكنك رؤية مستند يحتوي على “الحقل” الأخضر.

استخدم JavaScript للعثور على إحداثيات النوافذ ذات الزوايا المشار إليها بالسهام.

هناك ميزة صغيرة تم تنفيذها في المستند من أجل الراحة. تظهر نقرة في أي مكان إحداثيات هناك.

يجب أن يستخدم الرمز الخاص بك DOM للحصول على إحداثيات النافذة لـ:

  1. الزاوية العلوية اليسرى ، (هذا بسيط).
  2. الزاوية اليمنى السفلية ، (بسيطة أيضًا).
  3. الزاوية اليسرى العليا ، الداخلية (أصعب قليلاً).
  4. الزاوية اليمنى السفلية (هناك عدة طرق ، اختر واحدة).

يجب أن تكون الإحداثيات التي تحسبها هي نفس الإحداثيات التي تم إرجاعها بواسطة النقر بالماوس.

ملاحظة. يجب أن تعمل الشفرة أيضًا إذا كان للعنصر حجم أو حد آخر ، غير مرتبط بأي قيم ثابتة.

افتح sandbox للمهمه.

الزوايا الخارجية

الزوايا الخارجية هي في الأساس ما نحصل عليه من [elem.getBoundingClientRect ()] (https://developer.mozilla.org/en-US/docs/DOM/element.getBoundingClientRect).

إحداثيات الزاوية العلوية اليسرى answer1 والزاوية اليمنى السفلىanswer2:

let coords = elem.getBoundingClientRect();

let answer1 = [coords.left, coords.top];
let answer2 = [coords.right, coords.bottom];

الزاوية الداخلية اليسرى العليا

هذا يختلف عن الزاوية الخارجية بعرض الحدود. طريقة موثوقة للحصول على المسافة هي clientLeft / clientTop:

let answer3 = [coords.left + field.clientLeft, coords.top + field.clientTop];

الزاوية الداخلية اليمنى السفلية

في حالتنا ، نحتاج إلى طرح حجم الحدود من الإحداثيات الخارجية. يمكننا استخدام طريقة CSS:

let answer4 = [
  coords.right - parseInt(getComputedStyle(field).borderRightWidth),
  coords.bottom - parseInt(getComputedStyle(field).borderBottomWidth)
];

هناك طريقة بديلة تتمثل في إضافة “clientWidth / clientHeight” إلى إحداثيات الزاوية العلوية اليسرى. ربما هذا أفضل:

let answer4 = [
  coords.left + elem.clientLeft + elem.clientWidth,
  coords.top + elem.clientTop + elem.clientHeight
];

افتح الحل في sandbox.

إنشاء وظيفة positionAt (مرساة ، موضع ، elem) تضع مواضع elem ، اعتمادًا علىposition بالقرب من عنصر anchor.

يجب أن يكون “الموضع” عبارة عن سلسلة تحتوي على أي قيمة من 3 قيم:

  • `` top ‘’ – ضع elem فوقanchor مباشرة
  • “” right “” – ضع “elem” مباشرة على يمين “anchor”
  • “أسفل” “- ضع” elem “أسفل” anchor "مباشرة

يتم استخدامه داخل الوظيفة showNote (anchor ، position ، html) ، المتوفرة في شفرة مصدر المهمة ، والتي تنشئ عنصر “note” مع html معينًا وتعرضه في" الموضع “القريب من” anchor ".

إليك عرض الملاحظات:

افتح sandbox للمهمه.

في هذه المهمة نحتاج فقط لحساب الإحداثيات بدقة. انظر الرمز للحصول على التفاصيل.

يرجى ملاحظة: يجب أن تكون العناصر في المستند لقراءة offsetHeight وخصائص أخرى. العنصر المخفي (display: none) أو خارج عنصر المستند ليس له حجم.

افتح الحل في sandbox.

الأهمية: 5

قم بتعديل حل [المهمة السابقة] (info: task / position-at) بحيث تستخدم الملاحظة “الموضع: مطلق” بدلاً من “الموضع: ثابت”.

وذلك سيمنع “هربه” من العنصر عند تمرير الصفحة.

خذ حل هذه المهمة كنقطة انطلاق. لاختبار التمرير ، أضف النمط <body style =" height: 2000px ">.

الحل بسيط للغاية:

  • استخدم “الموضع: مطلق” في CSS بدلاً من “الموضع: ثابت” لـ “. ملاحظة”.
  • استخدم الوظيفة [getCoords ()] (info: إحداثيات # getCoords) من الفصل <info: إحداثيات> للحصول على إحداثيات نسبية للdocument.

افتح الحل في sandbox.

تمديد المهمة السابقة إظهار ملاحظة بالقرب من العنصر (مطلق): علم الوظيفة الموضع (المرساة ، الموضع ، العنصر) لإدراج عنصر داخلالمرساة.

القيم الجديدة لـ “الموضع”:

  • top-out وright-out وbottom-out – تعمل كما كانت من قبل ، حيث تقوم بإدخالelem over / right / under anchor.
  • “أعلى” ، “يمين” ، “أسفل” – أدخل “عنصر” داخل “المرساة”: ألصقه بالحافة العلوية / اليمنى / السفلية.

على سبيل المثال:

// shows the note above blockquote
positionAt(blockquote, "top-out", note);

// shows the note inside blockquote, at the top
positionAt(blockquote, "top-in", note);

النتائج:

كرمز المصدر ، خذ حل المهمة إظهار ملاحظة بالقرب من العنصر (مطلق).

خريطة الدورة التعليمية