Zrozumienie odpowiednio offsetWidth, clientWidth, scrollWidth i -Height

385

StackOverflow ma kilka pytań dotyczących offsetWidth / clientWidth / scrollWidth (odpowiednio i -Height), ale żadne z nich nie zawiera wyczerpującego wyjaśnienia, jakie są te wartości.

Istnieje również kilka źródeł w sieci, które zawierają mylące lub niepoprawne informacje.

Czy możesz podać pełne wyjaśnienie, w tym wskazówki wizualne? W jaki sposób można te wartości wykorzystać do obliczenia szerokości paska przewijania?

użytkownik123444555621
źródło

Odpowiedzi:

868

Model pudełkowy CSS jest dość skomplikowany, szczególnie jeśli chodzi o przewijanie treści. Podczas gdy przeglądarka używa wartości z twojego CSS do rysowania ramek, określenie wszystkich wymiarów za pomocą JS nie jest proste, jeśli masz tylko CSS.

Dlatego każdy element ma sześć właściwości DOM dla wygody: offsetWidth, offsetHeight, clientWidth, clientHeight, scrollWidthi scrollHeight. Są to atrybuty tylko do odczytu reprezentujące bieżący układ wizualny, a wszystkie z nich są liczbami całkowitymi (w związku z tym mogą wystąpić błędy zaokrąglania).

Przejrzyjmy je szczegółowo:

  • offsetWidth, offsetHeight: Rozmiar pola wizualnego zawierającego wszystkie obramowania. Można obliczyć, dodając width/ heighti wypełnienia i obramowania, jeśli element madisplay: block
  • clientWidth, clientHeight: Część wizualna zawartości pola, nie zawierająca ramek ani pasków przewijania, ale obejmuje dopełnienie. Nie można obliczyć bezpośrednio z CSS, zależy od rozmiaru paska przewijania systemu.
  • scrollWidth, scrollHeight: Rozmiar całej zawartości pola, w tym części, które są obecnie ukryte poza obszarem przewijania. Nie można obliczyć bezpośrednio z CSS, zależy od zawartości.

Model pudełkowy CSS2

Wypróbuj: jsFiddle


Ponieważ offsetWidthuwzględnia szerokość paska przewijania, możemy go użyć do obliczenia szerokości paska przewijania za pomocą formuły

scrollbarWidth = offsetWidth - clientWidth - getComputedStyle().borderLeftWidth - getComputedStyle().borderRightWidth

Niestety mogą wystąpić błędy zaokrąglania, ponieważ offsetWidthi clientWidthzawsze są liczbami całkowitymi, podczas gdy rzeczywiste rozmiary mogą być ułamkowe przy poziomach powiększenia innych niż 1.

Zauważ, że to

scrollbarWidth = getComputedStyle().width + getComputedStyle().paddingLeft + getComputedStyle().paddingRight - clientWidth

ma nie działać niezawodnie w Chrome, Chrome, ponieważ wraca widthz przewijania już odjąć. (Ponadto Chrome renderuje paddingBottom na dole przewijanej zawartości, podczas gdy inne przeglądarki tego nie robią)

użytkownik123444555621
źródło
27
Dla tych, którzy szukają drobniejszej ziarnistości niż liczby całkowite, użyj element.getBoundingClientRect()(patrz uwaga na developer.mozilla.org/en-US/docs/Web/API/Element.clientWidth )
Anson Kao
1
Zauważ, że w zależności od układu, scrollWidth i scrollHeight mogą być bardzo przydatne, aby uzyskać rozmiar twoich pseudoelementów :: przed i po.
David
Ponadto, dobrze byłoby wyjaśnić, w jaki sposób te odnoszą się do naturalWidthinaturalHeight
YakovL
dlaczego scrollHeightzawiera, padding-bottomale scrollWidthnie obejmujepadding-right
JunGor
clientWidthza document.documentElement.clientWidthto różne, jak to wydaje się zawierać padding, bordersorazmargin
Drenajów
49

Stworzyłem bardziej kompleksową i czystszą wersję, która może być przydatna dla niektórych osób, pamiętając, która nazwa odpowiada której wartości. Użyłem kodu koloru Chrome Dev Tool, a etykiety są zorganizowane symetrycznie, aby szybciej odbierać analogie:

wprowadź opis zdjęcia tutaj

  • Uwaga 1: clientLeftobejmuje również szerokość pionowego paska przewijania, jeśli kierunek tekstu jest ustawiony od prawej do lewej (w takim przypadku pasek jest wyświetlany w lewo)

  • Uwaga 2: najbardziej zewnętrzna linia reprezentuje najbliżej położonego rodzica (element, którego positionwłaściwość jest ustawiona na wartość inną niż staticlub initial). Zatem jeśli bezpośredni kontener nie jest elementem pozycjonowanym , to linia nie reprezentuje pierwszego kontenera w hierarchii, ale inny element wyżej w hierarchii. Jeśli nie zostanie znaleziony element nadrzędny pozycjonowany , przeglądarka przyjmie element htmllub bodyjako odniesienie


Mam nadzieję, że ktoś uzna to za przydatne, tylko moje 2 centy;)

Lual
źródło
30

Jeśli chcesz użyć scrollWidth, aby uzyskać „PRAWDZIWĄ” SZEROKOŚĆ / WYSOKOŚĆ TREŚCI (ponieważ treść może być WIĘKSZA niż zdefiniowana przez css szerokość / wysokość-Box), scrollWidth / Height jest bardzo NIEZAWODNA, ponieważ niektóre przeglądarki wydają się „PRZESUWAĆ” paddingRIGHT & paddingBOTTOM, jeśli treść jest za duża. Następnie umieszczają wypełnienia po PRAWEJ / DOLNEJ „zbyt szerokiej / wysokiej zawartości” (patrz obrazek poniżej).

==> Dlatego, aby uzyskać PRAWDZIWĄ SZEROKOŚĆ TREŚCI w niektórych przeglądarkach, musisz odjąć OBA wypełnienia od szerokości przewijania, aw niektórych przeglądarkach musisz tylko odjąć LEWĄ wypełnienie.

Znalazłem rozwiązanie tego problemu i chciałem dodać to jako komentarz, ale nie było to dozwolone. Zrobiłem więc zdjęcie i uczyniłem je nieco jaśniejszym w odniesieniu do „ruchomych podkładek” i „niewiarygodnego scrollWidth”. W NIEBIESKIEJ OBSZARZE znajdziesz moje rozwiązanie, jak uzyskać „PRAWDZIWĄ” SZEROKOŚĆ TREŚCI!

Mam nadzieję, że dzięki temu wszystko będzie jeszcze wyraźniejsze!

wprowadź opis zdjęcia tutaj

Użytkownik manny3297459
źródło
13

Istnieje dobry artykuł na temat MDN, który wyjaśnia teorię tych pojęć: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements

Wyjaśnia także ważne koncepcyjne różnice między szerokością / wysokością boundingClientRect a offsetWidth / offsetHeight.

Następnie, aby udowodnić, że teoria jest dobra lub zła, potrzebujesz testów. To właśnie zrobiłem tutaj: https://github.com/lingtalfi/dimensions-cheatsheet

Testuje pod kątem chrome53, ff49, safari9, edge13 i ie11.

Wyniki testów dowodzą, że teoria jest ogólnie słuszna. Do testów stworzyłem 3 div zawierające po 10 akapitów lorem ipsum. Zastosowano do nich trochę css:

.div1{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    overflow: auto;
}
.div2{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    box-sizing: border-box;
    overflow: auto;
}

.div3{
    width: 500px;
    height: 300px;
    padding: 10px;
    border: 5px solid black;
    overflow: auto;
    transform: scale(0.5);
}

A oto wyniki:

  • div1

    • offsetWidth: 530 (chrome53, ff49, safari9, edge13, ie11)
    • offsetHeight: 330 (chrome53, ff49, safari9, edge13, ie11)
    • bcr. szerokość: 530 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.height: 330 (chrome53, ff49, safari9, edge13, ie11)

    • clientWidth: 505 (chrome53, ff49, safari9)

    • clientWidth: 508 (edge13)
    • clientWidth: 503 (ie11)
    • clientHeight: 320 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 505 (chrome53, safari9, ff49)

    • scrollWidth: 508 (edge13)
    • scrollWidth: 503 (ie11)
    • scrollHeight: 916 (chrome53, safari9)
    • scrollHeight: 954 (ff49)
    • scrollHeight: 922 (edge13, ie11)
  • div2

    • offsetWidth: 500 (chrome53, ff49, safari9, edge13, ie11)
    • offsetHeight: 300 (chrome53, ff49, safari9, edge13, ie11)
    • bcr. szerokość: 500 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.height: 300 (chrome53, ff49, safari9)
    • bcr.height: 299.9999694824219 (edge13, ie11)
    • clientWidth: 475 (chrome53, ff49, safari9)
    • clientWidth: 478 (edge13)
    • clientWidth: 473 (ie11)
    • clientHeight: 290 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 475 (chrome53, safari9, ff49)

    • scrollWidth: 478 (edge13)
    • scrollWidth: 473 (ie11)
    • scrollHeight: 916 (chrome53, safari9)
    • scrollHeight: 954 (ff49)
    • scrollHeight: 922 (edge13, ie11)
  • div3

    • offsetWidth: 530 (chrome53, ff49, safari9, edge13, ie11)
    • offsetHeight: 330 (chrome53, ff49, safari9, edge13, ie11)
    • bcr. szerokość: 265 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.height: 165 (chrome53, ff49, safari9, edge13, ie11)
    • clientWidth: 505 (chrome53, ff49, safari9)
    • clientWidth: 508 (edge13)
    • clientWidth: 503 (ie11)
    • clientHeight: 320 (chrome53, ff49, safari9, edge13, ie11)

    • scrollWidth: 505 (chrome53, safari9, ff49)

    • scrollWidth: 508 (edge13)
    • scrollWidth: 503 (ie11)
    • scrollHeight: 916 (chrome53, safari9)
    • scrollHeight: 954 (ff49)
    • scrollHeight: 922 (edge13, ie11)

Tak więc, oprócz wartości wysokości boundingClientRect (299,9999694824219 zamiast oczekiwanych 300) w edge13 i ie11, wyniki potwierdzają, że teoria leżąca u podstaw tego działania działa.

Stamtąd oto moja definicja tych pojęć:

  • offsetWidth / offsetHeight: wymiary ramki granicznej układu
  • boundingClientRect: wymiary ramki renderowania
  • clientWidth / clientHeight: wymiary widocznej części pola dopełnienia układu (bez pasków przewijania)
  • scrollWidth / scrollHeight: wymiary pola dopełnienia układu, jeśli nie było ograniczone paskami przewijania

Uwaga: domyślna szerokość pionowego paska przewijania wynosi 12px w edge13, 15px w chrome53, ff49 i safari9 oraz 17px w ie11 (wykonane na podstawie pomiarów w Photoshopie ze zrzutów ekranu i potwierdzone bezpośrednio na podstawie wyników testów).

Jednak w niektórych przypadkach być może Twoja aplikacja nie używa domyślnej szerokości pionowego paska przewijania.

Biorąc pod uwagę definicje tych pojęć, szerokość pionowego paska przewijania powinna być równa (w pseudokodzie):

  • wymiar układu: offsetWidth - clientWidth - (borderLeftWidth + borderRightWidth)

  • wymiar renderowania: boundingClientRect.width - clientWidth - (borderLeftWidth + borderRightWidth)

Uwaga: jeśli nie rozumiesz układu vs renderowania, przeczytaj artykuł mdn.

Ponadto, jeśli masz inną przeglądarkę (lub jeśli chcesz zobaczyć wyniki testów dla siebie), możesz zobaczyć moją stronę testową tutaj: http://codepen.io/lingtalfi/pen/BLdBdL

molwa
źródło