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

حجم العنصر والتمرير

هناك العديد من خصائص JavaScript التي تسمح لنا بقراءة معلومات حول عرض العنصر وارتفاعه وميزات الهندسة الأخرى.

غالبًا ما نحتاج إليها عند تحريك العناصر أو وضعها في JavaScript.

عنصر العينة

كعنصر عينة لإثبات الخصائص ، سنستخدم الخاصية الواردة أدناه:

<div id="example">
  ...Text...
</div>
<style>
  #example {
    width: 300px;
    height: 200px;
    border: 25px solid #E8C48F;
    padding: 20px;
    overflow: auto;
  }
</style>

لديها الحدود والحشو والتمرير. مجموعة كاملة من الميزات. لا توجد هوامش ، لأنها ليست جزءًا من العنصر نفسه ، ولا توجد خصائص خاصة لهم.

يبدو العنصر كما يلي:

You can open the document in the sandbox.

Mind the scrollbar

توضح الصورة أعلاه الحالة الأكثر تعقيدًا عندما يكون للعنصر شريط تمرير. تحتفظ بعض المتصفحات (وليس كلها) بالفضاء الخاص بها من خلال أخذها من المحتوى (المسمى “عرض المحتوى” أعلاه).

لذلك ، بدون شريط التمرير ، سيكون عرض المحتوى 300 بكسل ، ولكن إذا كان شريط التمرير بعرض16 بكسل (قد يختلف العرض بين الأجهزة والمتصفحات) ، فعندئذٍ يبقى 300 - 16 = 284 بكسل فقط ، ويجب أن نأخذه في الاعتبار . لهذا السبب تفترض أمثلة من هذا الفصل وجود شريط تمرير. بدونه ، بعض الحسابات أبسط.

The padding-bottom aقد تكون مليئة بالنص

عادةً ما يتم عرض الحشو فارغة على الرسوم التوضيحية الخاصة بنا ، ولكن إذا كان هناك الكثير من النص في العنصر وتجاوزه ، فإن المتصفحات تعرض النص “الفائض” في “الحشو السفلي” ، فهذا أمر طبيعي.

الهندسة

فيما يلي الصورة العامة بخصائص الهندسة:

قيم هذه الخصائص هي أرقام تقنيًا ، لكن هذه الأرقام هي “بكسل” ، لذا فهي قياسات بكسل.

لنبدأ في استكشاف الخصائص بدءًا من خارج العنصر.

offsetParent, offsetLeft/Top

نادرًا ما تكون هذه الخصائص مطلوبة ، لكنها لا تزال هي الخصائص الهندسية “الخارجية” ، لذلك سنبدأ بها.

يعد “offsetParent” أقرب سلف يستخدمه المتصفح لحساب الإحداثيات أثناء العرض.

هذا هو أقرب سلف وهو واحد مما يلي:

1- وضع CSS (“الموضع” هو “مطلق” ، “قريب” ، “ثابت” أو “ثابت”) ، أو 2. <td> أو <th> أو <table> أو 3. <body>.

توفر الخصائص offsetLeft / offsetTop إحداثيات س / ص نسبة إلىoffsetParent` الزاوية العلوية اليسرى.

في المثال أدناه ، يحتوي <div> الداخلي على <main> كـ offsetParent وoffsetLeft / offsetTop من الزاوية العلوية اليسرى (180):

<main style="position: relative" id="main">
  <article>
    <div id="example" style="position: absolute; left: 180px; top: 180px">...</div>
  </article>
</main>
<script>
  alert(example.offsetParent.id); // main
  alert(example.offsetLeft); // 180 (note: a number, not a string "180px")
  alert(example.offsetTop); // 180
</script>

هناك عدة مناسبات عندما يكون “offsetParent” “فارغًا”:

  1. للعناصر غير المعروضة (display: none أو not in the document).
  2. بالنسبة لـ "و "<html>. 3- للعناصر ذات “الموضع: ثابت”.

offsetWidth/Height

الآن دعنا ننتقل إلى العنصر نفسه.

هاتان الخاصيتان هما الأبسط. توفر العرض / الارتفاع “الخارجي” للعنصر. أو بعبارة أخرى حجمها الكامل بما في ذلك الحدود.

لعنصر العينة لدينا:

  • offsetWidth = 390 – العرض الخارجي ، يمكن حسابه على أنه عرض CSS داخلي (300 بكسل) بالإضافة إلى حشوات (2 * 20 بكسل) وحدود (2 * 25 بكسل).
  • offsetHeight = 290 – الارتفاع الخارجي.
خصائص الهندسة هي صفر / قيمة للعناصر التي لا يتم عرضها

يتم حساب خصائص الهندسة فقط للعناصر المعروضة.

إذا كان أحد العناصر (أو أي من أسلافه) يحتوي على “عرض: لا شيء” أو غير موجود في المستند ، فإن جميع الخصائص الهندسية تكون صفرية (أو “خالية” لـ “offsetParent”).

على سبيل المثال ، offsetParent هيnull ، و offsetWidth ،offsetHeight هي 0 عندما أنشأنا عنصرًا ، ولكننا لم نقم بإدراجه في المستند حتى الآن ، أو (أو أصله) يحتوي على` عرض : لا شيء.

يمكننا استخدام هذا للتحقق مما إذا كان العنصر مخفيًا ، مثل هذا:

function isHidden(elem) {
  return !elem.offsetWidth && !elem.offsetHeight;
}

يرجى ملاحظة أن مثل isHidden يعرضtrue للعناصر التي تظهر على الشاشة ، ولكن بدون أحجام (مثل <div> فارغ).

clientTop/Left

داخل العنصر لدينا الحدود.

لقياسها ، هناك خصائص clientTop وclientLeft.

في مثالنا:

  • clientLeft = 25 – left border width
  • clientTop = 25 – top border width

ولكن على وجه الدقة – هذه الخصائص ليست عرض / ارتفاع الحد ، بل هي إحداثيات نسبية للجانب الداخلي من الجانب الخارجي.

ماهو الفرق؟

تصبح مرئية عندما يكون المستند من اليمين إلى اليسار (نظام التشغيل باللغة العربية أو العبرية). شريط التمرير ليس بعد ذلك على اليمين ، ولكن على اليسار ، ثم يتضمن “clientLeft” أيضًا عرض شريط التمرير.

في هذه الحالة ، لن يكون “clientLeft” 25 ، ولكن بعرض شريط التمرير25 + 16 = 41.

هذا هو المثال بالعبرية:

clientWidth/Height

توفر هذه الخصائص حجم المنطقة داخل حدود العنصر.

تتضمن عرض المحتوى مع الحشو ، لكن بدون شريط التمرير:

في الصورة أعلاه ، لنفكر أولاً في “clientHeight”.

لا يوجد شريط تمرير أفقي ، لذلك فهو بالضبط مجموع ما يوجد داخل الحدود: ارتفاع CSS 200 بكسل بالإضافة إلى حشوات علوية وسفلية (2 * 20 بكسل) إجمالي 240 بكسل.

الآن “clientWidth” – هنا ليس عرض المحتوى “300 بكسل” ، ولكن “284 بكسل” ، لأن “16 بكسل” مشغول بشريط التمرير. لذلك يكون المجموع 284 بكسل بالإضافة إلى حشوات اليسار واليمين ، إجمالي324 بكسل.

** إذا لم تكن هناك حشوات ، فإن “clientWidth / Height” هي بالضبط منطقة المحتوى ، داخل الحدود وشريط التمرير (إن وجد). **

لذلك عندما لا يكون هناك أي مساحة ، يمكننا استخدام clientWidth / clientHeight للحصول على حجم منطقة المحتوى.

scrollWidth/Height

هذه الخصائص مثل clientWidth / clientHeight ، ولكنها تشمل أيضًا الأجزاء التي تم تمريرها (المخفية):

الصورة "/article/size-and-scroll/metric-roll-width-height.svg" غير موجودة

في الصورة أعلاه:

  • التمرير = 723 – الارتفاع الداخلي الكامل لمنطقة المحتوى بما في ذلك الأجزاء التي تم تمريرها.
  • التمرير العرضي = 324 – هو العرض الداخلي الكامل ، وهنا ليس لدينا تمرير أفقي ، لذا فهو يساويclientWidth.

يمكننا استخدام هذه الخصائص لتوسيع العنصر إلى عرضه / ارتفاعه الكامل.

مثله:

// expand the element to the full content height
element.style.height = `${element.scrollHeight}px`;

انقر فوق الزر لمد العنصر:

text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text

scrollLeft/scrollTop

الخصائص التمرير لليسار / التمرير العلوي هي عرض / ارتفاع الجزء المخفي من العنصر.

في الصورة أدناه ، يمكننا رؤية “التمرير” و “التمرير العلوي” للكتلة ذات التمرير العمودي.

وبعبارة أخرى ، فإن “rollTop” هو “مقدار التمرير”.

يمكن تعديل

معظم الخصائص الهندسية هنا للقراءة فقط ، ولكن يمكن تغيير التمرير لليسار / التمرير العلوي ، وسوف يقوم المتصفح بتمرير العنصر.

إذا قمت بالنقر فوق العنصر أدناه ، فسيتم تنفيذ الرمز elem.scrollTop + = 10. هذا يجعل محتوى العنصر بالتمرير 10px للأسفل.

Click
Me
1
2
3
4
5
6
7
8
9

يؤدي تعيين “scrollTop” إلى “0” أو “إنفينيتي” إلى جعل العنصر ينتقل إلى الأعلى/الأسفل على التوالي.

لا تأخذ العرض / الارتفاع من CSS

لقد غطينا للتو الخصائص الهندسية لعناصر DOM ، والتي يمكن استخدامها للحصول على العرض والارتفاعات وحساب المسافات.

ولكن كما نعلم من الفصل <info: styles-and-classes> ، يمكننا قراءة ارتفاع وعرض CSS باستخدام getComputedStyle.

فلماذا لا تقرأ عرض عنصر باستخدام getComputedStyle ، مثل هذا؟

let elem = document.body;

alert( getComputedStyle(elem).width ); // show CSS width for elem

لماذا يجب أن نستخدم الخصائص الهندسية بدلاً من ذلك؟ هناك سببان:

  1. أولاً ، يعتمد “عرض / ارتفاع” CSS على خاصية أخرى: “تحجيم المربع” الذي يحدد “ما هو” عرض وارتفاع CSS. قد يؤدي التغيير في “تحجيم المربع” لأغراض CSS إلى كسر جافا سكريبت.

  2. ثانيًا ، قد يكون “عرض / ارتفاع” CSS “تلقائي” ، على سبيل المثال لعنصر مضمن:

    <span id="elem">Hello!</span>
    
    <script>
      alert( getComputedStyle(elem).width ); // auto
    </script>

    من وجهة نظر CSS ، يعد العرض: تلقائي أمرًا طبيعيًا تمامًا ، ولكن في جافا سكريبت نحتاج إلى حجم دقيق فيبكسل يمكننا استخدامه في العمليات الحسابية. حتى هنا عرض CSS عديم الفائدة.

وهناك سبب آخر: شريط التمرير. في بعض الأحيان ، تصبح الشفرة التي تعمل بشكل جيد بدون شريط تمرير عربات التي تجرها الدواب ، لأن شريط التمرير يأخذ المساحة من المحتوى في بعض المتصفحات. لذا فإن العرض الحقيقي المتاح للمحتوى هو * أقل * من عرض CSS. و “clientWidth / clientHeight” يأخذ ذلك في الاعتبار.

… ولكن مع “getComputedStyle (elem) .width” ، الوضع مختلف. تعرض بعض المتصفحات (مثل Chrome) العرض الداخلي الحقيقي مطروحًا منه شريط التمرير ، وبعضها (مثل Firefox) – عرض CSS (تجاهل شريط التمرير). هذه الاختلافات بين المتصفحات هي السبب في عدم استخدام getComputedStyle ، بل الاعتماد على الخصائص الهندسية.

إذا كان المستعرض الخاص بك يحتفظ بمساحة شريط التمرير (معظم المستعرضات لنظام Windows) ، فيمكنك اختباره أدناه.

يحتوي العنصر الذي يحتوي على نص على CSS width: 300px.

على نظام التشغيل Windows Desktop و Firefox و Chrome و Edge ، كلهم ​​يحجزون مساحة شريط التمرير. لكن Firefox يُظهر 300 بكسل ، بينما يُظهر Chrome و Edge أقل. ذلك لأن Firefox يعرض عرض CSS وتعرض المستعرضات الأخرى العرض “الحقيقي”. ``

يرجى ملاحظة أن الفرق الموضح هو فقط حول قراءة getComputedStyle (...). width من JavaScript ، كل شيء بصريًا صحيح.

الملخص

العناصر لها خصائص الهندسة التالية:

  • offsetParent – أقرب سلف تم وضعه أوtd أو th أوtable أو body.
  • offsetLeft / offsetTop – الإحداثيات المتعلقة بالحافة العلوية اليسرى لـoffsetParent.
  • “offsetWidth / offsetHeight” – العرض / الارتفاع “الخارجي” للعنصر بما في ذلك الحدود.
  • clientLeft / clientTop – المسافات من الزاوية الخارجية العلوية اليسرى إلى الزاوية الداخلية العلوية اليسرى (المحتوى + المساحة). بالنسبة لنظام التشغيل من اليسار إلى اليمين ، تكون دائمًا عروض الحدود اليسرى / العلوية. بالنسبة لنظام التشغيل من اليمين إلى اليسار ، يكون شريط التمرير الرأسي على اليسار بحيث يتضمن “clientLeft” عرضه أيضًا.
  • “clientWidth / clientHeight” – عرض / ارتفاع المحتوى بما في ذلك البطانات ، ولكن بدون شريط التمرير.
  • “التمرير / التمرير” – عرض / ارتفاع المحتوى ، مثل “clientWidth / clientHeight” ، ولكن يشمل أيضًا الجزء غير المرئي من العنصر.
  • التمرير لليسار / التمرير العلوي – عرض / ارتفاع الجزء العلوي الذي تم تمريره للخارج من العنصر ، بدءًا من الزاوية العلوية اليسرى.

جميع الخصائص للقراءة فقط باستثناء التمرير الأيسر / التمرير العلوي والتي تجعل المتصفح يقوم بتمرير العنصر إذا تم تغييره.

مهمه

الأهمية: 5

The elem.scrollTop property is the size of the scrolled out part from the top. How to get the size of the bottom scroll (let’s call it scrollBottom)?

Write the code that works for an arbitrary elem.

P.S. Please check your code: if there’s no scroll or the element is fully scrolled down, then it should return 0.

The solution is:

let scrollBottom = elem.scrollHeight - elem.scrollTop - elem.clientHeight;

In other words: (full height) minus (scrolled out top part) minus (visible part) – that’s exactly the scrolled out bottom part.

الأهمية: 3

Write the code that returns the width of a standard scrollbar.

For Windows it usually varies between 12px and 20px. If the browser doesn’t reserve any space for it (the scrollbar is half-translucent over the text, also happens), then it may be 0px.

P.S. The code should work for any HTML document, do not depend on its content.

To get the scrollbar width, we can create an element with the scroll, but without borders and paddings.

Then the difference between its full width offsetWidth and the inner content area width clientWidth will be exactly the scrollbar:

// create a div with the scroll
let div = document.createElement('div');

div.style.overflowY = 'scroll';
div.style.width = '50px';
div.style.height = '50px';

// must put it in the document, otherwise sizes will be 0
document.body.append(div);
let scrollWidth = div.offsetWidth - div.clientWidth;

div.remove();

alert(scrollWidth);
الأهمية: 5

Here’s how the source document looks:

What are coordinates of the field center?

Calculate them and use to place the ball into the center of the green field:

  • The element should be moved by JavaScript, not CSS.
  • The code should work with any ball size (10, 20, 30 pixels) and any field size, not be bound to the given values.

P.S. Sure, centering could be done with CSS, but here we want exactly JavaScript. Further we’ll meet other topics and more complex situations when JavaScript must be used. Here we do a “warm-up”.

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

The ball has position:absolute. It means that its left/top coordinates are measured from the nearest positioned element, that is #field (because it has position:relative).

The coordinates start from the inner left-upper corner of the field:

The inner field width/height is clientWidth/clientHeight. So the field center has coordinates (clientWidth/2, clientHeight/2).

…But if we set ball.style.left/top to such values, then not the ball as a whole, but the left-upper edge of the ball would be in the center:

ball.style.left = Math.round(field.clientWidth / 2) + 'px';
ball.style.top = Math.round(field.clientHeight / 2) + 'px';

Here’s how it looks:

To align the ball center with the center of the field, we should move the ball to the half of its width to the left and to the half of its height to the top:

ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px';
ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px';

Now the ball is finally centered.

Attention: the pitfall!

The code won’t work reliably while <img> has no width/height:

<img src="ball.png" id="ball">

When the browser does not know the width/height of an image (from tag attributes or CSS), then it assumes them to equal 0 until the image finishes loading.

So the value of ball.offsetWidth will be 0 until the image loads. That leads to wrong coordinates in the code above.

After the first load, the browser usually caches the image, and on reloads it will have the size immediately. But on the first load the value of ball.offsetWidth is 0.

We should fix that by adding width/height to <img>:

<img src="ball.png" width="40" height="40" id="ball">

…Or provide the size in CSS:

#ball {
  width: 40px;
  height: 40px;
}

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

الأهمية: 5

What’s the difference between getComputedStyle(elem).width and elem.clientWidth?

Give at least 3 differences. The more the better.

Differences:

  1. clientWidth is numeric, while getComputedStyle(elem).width returns a string with px at the end.
  2. getComputedStyle may return non-numeric width like "auto" for an inline element.
  3. clientWidth is the inner content area of the element plus paddings, while CSS width (with standard box-sizing) is the inner content area without paddings.
  4. If there’s a scrollbar and the browser reserves the space for it, some browser substract that space from CSS width (cause it’s not available for content any more), and some do not. The clientWidth property is always the same: scrollbar size is substracted if reserved.
خريطة الدورة التعليمية