# Element+boxes

[browser](../../..) ⟩ [DOM](../..) ⟩ [type](..) ⟩ [Element]() ⟩ +boxes&#x20;

{% hint style="success" %}
extend [Element]() with various [**box models**](boxes).

* [padding](+boxes/padding "mention") = <mark style="color:yellow;">**content**</mark> + <mark style="color:orange;">**padding**</mark>
* [border](+boxes/border "mention") = <mark style="color:yellow;">**content**</mark> + <mark style="color:orange;">**padding**</mark> + <mark style="color:blue;">**border**</mark> (+ <mark style="color:red;">**scrollbars**</mark>, if present)\
  (a [**Rect**](../../../../appendix/custom/class/rect) relative to .[offsetParent](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent))
* [bounding](+boxes/bounding "mention") - a [**Rect**](../../../../appendix/custom/class/rect) relative to [**viewport**](../window/viewport).
* [scroll](+boxes/scroll "mention") = <mark style="color:yellow;">**content**</mark> + <mark style="color:orange;">**padding**</mark> + <mark style="color:red;">**overflow**</mark>
* [viewport](../window/viewport "mention")

:point\_right: [custom](../../../../appendix/custom "mention")
{% endhint %}

{% tabs %}
{% tab title="🗺️ 圖表" %}
:point\_right: compare： [mouse](../../../event/type/mouse "mention"), [elem.position](+ext/elem.position "mention")

<img src="https://2527454625-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MfvEFZnSBhKT6fJmus0%2Fuploads%2FuIz4w8DHIuWAyAlrkbsP%2Felement%2Bboxes.svg?alt=media&#x26;token=58faf8bb-c454-48ac-ae48-5f70eb363679" alt="Element+boxes" class="gitbook-drawing">
{% endtab %}

{% tab title="💾 程式" %}
* replit ⟩ [various boxes](https://replit.com/@pegasusroe/docviewport-size-2#script.js) ,  require -> [rect](../../../../appendix/custom/class/rect "mention")

```javascript
// 2023.01.18 - 16:30 (+) ._box (refactor), .toString(), viewport
// 2023.01.17 - 21:09 (•) first draft
// -----------------------------------------------

const {log} = console;

// ⭐️ import
import {rect} from './Rect.js';       // 👔 Rect
// -----------------------------------------------

// ⭐️ viewport
// -----------------------------------------------
// .size, .width, .height
// .origin, .x, .y
// -----------------------------------------------
// .toString()
//
const viewport = {

    // private
    get _box() {
        const {scrollX: x, scrollY: y} = window;
        const {innerWidth: w, innerHeight: h} = window;
        return rect(x, y, w, h);              // 👔 Rect
    },
    
    // 🔸 .size
    get size() { return this._box.size },
    get width() { return this.size.width },   // .width
    get height() { return this.size.height }, // .height
    
    // 🔸 .origin (relative to document)
    get origin() { return this._box.origin },
    get x() { return this.origin.x },         // .x
    get y() { return this.origin.y },         // .y

    // .toString()
    toString() { return this._box.toString() }
};

// ⭐️ Element + box models
// -----------------------------------------------
// 🔸 .paddingBox     - Rect properties + .info
// 🔸 .borderBox      - Rect properties + .info
// 🔸 .boundingBox    - Rect properties + .info
// 🔸 .scrollBox
//
Object.defineProperties(Element.prototype, {
    
    // 🔸 .paddingBox
    paddingBox: {        
        get() {
            const {clientLeft: x, clientTop: y} = this;
            const {clientWidth: width, clientHeight: height} = this;
            const box = rect(x, y, width, height);        // 👔 Rect
            
            box.info = [
                `🔲 padding box`,
                `---------------`,
                `• size: ${box.size}`,
                `  (❗ inline elements / elements with no CSS: return (0,0))`,
                `• origin: ${box.origin}`,
                `  (⭐️ relative to its "border box")`,
                `  (❗ inline elements (like <i>, <code> ...): return (0,0))`,
            ].join('\n');
            
            return box;
        },
    },

    // 🔸 .borderBox
    borderBox: {
        get() {
            
            const {offsetLeft: x, offsetTop: y} = this;
            const {offsetWidth: width, offsetHeight: height} = this;
            const box = rect(x, y, width, height);        // 👔 Rect
            
            box.info = [
                `🔲 border box`,
                `---------------`,
                `• size: ${box.size}`,
                `• origin: ${box.origin}`,
                `  (⭐️ relative to its "offsetParent")`,
                `• offsetParent: ${this.offsetParent ?. nodeName ?? 'null'}`,
                `  (❗ returns "null" if the element: )`,
                `    • display : none (or its parent)`,
                `    • position: fixed`,
                `    • is <html> or <body>`,
            ].join('\n');
            
            return box;
        },
    },

    // 🔸 .boundingBox
    boundingBox: {
        get() {
            
            const {x, y, width, height} = this.getBoundingClientRect();
            const box = rect(x, y, width, height);        // 👔 Rect
            
            box.info = [
                `🔲 bounding box`,
                `----------------`,
                `• size: ${box.size}`,
                `• origin: ${box.origin}`,
                `  (⭐️ relative to the "viewport")`,
            ].join('\n');
            
            return box;
        },
    },

    // 🔸 .scrollBox
    scrollBox: {
        get() { 
            
            const {scrollLeft: x, scrollTop: y} = this;
            const {scrollWidth: width, scrollHeight: height} = this;
            const box = rect(x, y, width, height);        // 👔 Rect
            
            box.info = [
                `🔲 scroll box`,
                `----------------`,
                `• size: ${box.size}`,
                `  (= padding box size, if no overflow.)`,
                `• origin: ${box.origin}`,
                `  (⭐️ relative to its "origin", = (0,0) if it can't scroll.)`,
            ].join('\n');
            
            return box;
        },
    },
    
});

// export
export {viewport};    // ES module export
```
{% endtab %}

{% tab title="👥 相關" %}
* [boxes](boxes "mention")
* [viewport](../window/viewport "mention")
* [elem.position](+ext/elem.position "mention")
* [window](../window "mention")
{% endtab %}

{% tab title="💈範例" %}
* [scroll-progress](../../../event/type/scroll/scroll-progress "mention") - use viewport.
* [scroll-me](../../../event/type/scroll/scroll-me "mention") - use .paddingBox, .scrollBox.
* [click-to-move-ball](../../../event/type/mouse/click/click-to-move-ball "mention")
{% endtab %}

{% tab title="📗 參考" %}
* [ ] [HTML vs Body: How to Set Width and Height for Full Page Size](https://www.freecodecamp.org/news/html-page-width-height/)
{% endtab %}

{% tab title="📘 手冊" %}
* [](<> "mention") ⟩&#x20;
  * [.clientWidth](https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth), [.clientHeight](https://developer.mozilla.org/en-US/docs/Web/API/Element/clientHeight)
  * [.scrollLeft](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft), [.scrollTop](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop), [.scrollTo()](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo), [.scrollWidth](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollWidth), [.scrollHeight](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight)
* [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) ⟩ [.offsetParent](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent)
* [window](../window "mention") ⟩&#x20;
  * .[scrollX](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollX), .[scrollY](https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY)&#x20;
{% endtab %}
{% endtabs %}

## History

{% tabs %}
{% tab title="idx" %}
```
0: (?) first recorded
```
{% endtab %}

{% tab title="0" %}
```javascript
// ⭐️ viewport
const viewport = {
    // .size
    get size() {
        return vec(innerWidth, innerHeight);
    },
    // .positionInDocument
    get positionInDocument() {
        return vec(scrollX, scrollY);
    }
};

// ⭐️ extending `Element`
// 🔸 .paddingBox
// 🔸 .borderBox
// 🔸 .boundingBox ≈ .borderBox (integer-valued)
// 🔸 .scrollBox
Object.defineProperties(Element.prototype, {
    
    // key = "paddingBox"
    paddingBox: {        
        // access descriptor ()
        get() {
            // save element for future reference
            const elem = this;    
            // return an object
            return {
                // .paddingBox.size (Vector)
                get size() {
                    return vec(elem.clientWidth, elem.clientHeight);
                },
            };
        },
    },

    // key = "borderBox"
    borderBox: {
        // access descriptor
        get() {
            // save element for future reference
            const elem = this;
            // return an object
            return {
                // .borderBox.size (Vector)
                get size() {
                    return vec(elem.offsetWidth, elem.offsetHeight);
                },
                // .borderBox.position()
                position(coordSystem){
                    // #TODO <----------------------- 
                    switch(coordSystem){
                        default: return vec(elem.offsetLeft, elem.offsetTop)
                    }
                }
            };
        },
    },

    // key = "boundingBox"
    boundingBox: {
        // access descriptor
        get() {
            // save element for future reference
            const elem = this;
            // return an object
            return {
                // .boundingBox.size (Vector)
                get size() {
                    const {width, height} = elem.getBoundingClientRect();
                    return vec(width, height);
                },
            };
        },
    },

    // key = "scrollBox"
    scrollBox: {
        // access descriptor
        get() {
            // save element for future reference
            const elem = this;    
            //return an object
            return {
                // .scrollBox.size (Vector)
                get size() {
                    return vec(elem.scrollWidth, elem.scrollHeight);
                },
            };
        },
    },
    
});bo
```
{% endtab %}
{% endtabs %}
