Noções básicas sobre offsetWidth, clientWidth, scrollWidth e -Height, respectivamente

385

Há várias perguntas no StackOverflow sobre offsetWidth / clientWidth / scrollWidth (e -Height, respectivamente), mas nenhuma fornece explicações detalhadas sobre quais são esses valores.

Além disso, existem várias fontes na web que fornecem informações confusas ou incorretas.

Você pode dar uma explicação completa, incluindo algumas dicas visuais? Além disso, como esses valores podem ser usados ​​para calcular as larguras da barra de rolagem?

user123444555621
fonte

Respostas:

869

O modelo da caixa CSS é bastante complicado, principalmente quando se trata de rolagem de conteúdo. Enquanto o navegador usa os valores do CSS para desenhar caixas, determinar todas as dimensões usando JS não é simples se você tiver apenas o CSS.

É por isso que cada elemento tem seis propriedades DOM para sua conveniência: offsetWidth, offsetHeight, clientWidth, clientHeight, scrollWidthe scrollHeight. Esses são atributos somente leitura que representam o layout visual atual e todos são números inteiros (possivelmente sujeitos a erros de arredondamento).

Vamos examiná-los em detalhes:

  • offsetWidth, offsetHeight: O tamanho da caixa visual incluindo todas as bordas. Pode ser calculado adicionando width/ heighte preenchimentos e bordas, se o elemento tiverdisplay: block
  • clientWidth, clientHeight : A parte visual do conteúdo da caixa, sem incluir bordas ou barras de rolagem, mas inclui preenchimento. Não pode ser calculado diretamente do CSS, depende do tamanho da barra de rolagem do sistema.
  • scrollWidth, scrollHeight: O tamanho de todo o conteúdo da caixa, incluindo as partes que estão ocultas atualmente fora da área de rolagem. Não pode ser calculado diretamente do CSS, depende do conteúdo.

Modelo de caixa CSS2

Experimente: jsFiddle


Como offsetWidthleva em conta a largura da barra de rolagem, podemos usá-la para calcular a largura da barra de rolagem por meio da fórmula

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

Infelizmente, podemos obter erros de arredondamento, uma vez offsetWidthe clientWidthsão sempre inteiros, enquanto os tamanhos reais podem ser fracionada com outros de 1 níveis de zoom.

Note que este

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

se não funcionar de forma confiável em Chrome, uma vez que os retornos do Chrome widthcom barra de rolagem já subtraídos. (Além disso, o Chrome renderiza paddingBottom na parte inferior do conteúdo da rolagem, enquanto outros navegadores não)

user123444555621
fonte
27
Para aqueles que procuram granularidade mais fina do que números inteiros, o uso element.getBoundingClientRect()(veja a nota no developer.mozilla.org/en-US/docs/Web/API/Element.clientWidth )
Anson Kao
11
Observe que, dependendo do seu layout, scrollWidth e scrollHeight podem ser realmente úteis para obter o tamanho dos seus pseudo-elementos :: before e :: after.
David
Além disso, seria útil para explicar como é que os relacionam com naturalWidthenaturalHeight
YakovL
por que scrollHeightinclui, padding-bottommas scrollWidthnão inclui?padding-right
JunGor
clientWidthpara document.documentElement.clientWidthé diferente, uma vez que parece incluir o padding, bordersemargin
Drenai
50

Criei uma versão mais abrangente e mais limpa que algumas pessoas podem achar útil para lembrar qual nome corresponde a qual valor. Usei o código de cores e os rótulos da Chrome Dev Tool são organizados simetricamente para captar analogias mais rapidamente:

insira a descrição da imagem aqui

  • Nota 1: clientLefttambém inclui a largura da barra de rolagem vertical se a direção do texto estiver definida da direita para a esquerda (já que a barra é exibida à esquerda nesse caso)

  • Nota 2: a linha mais externa representa o pai mais próximo posicionado (um elemento cuja positionpropriedade está configurada para um valor diferente de staticou initial). Portanto, se o contêiner direto não for um elemento posicionado , a linha não representa o primeiro contêiner na hierarquia, mas outro elemento mais alto na hierarquia. Se nenhum pai posicionado for encontrado, o navegador utilizará o elemento htmlou bodycomo referência


Espero que alguém ache útil, apenas meus 2 centavos;)

Lual
fonte
30

Se você deseja usar scrollWidth para obter a LARGURA / ALTURA "REAL" DE CONTEÚDO (como o conteúdo pode ser MAIOR do que a largura / altura-caixa definida por css), a largura / altura do scroll é muito INCRÍVEL, pois alguns navegadores parecem "MOVER" o preenchimento & paddingBOTTOM se o conteúdo for grande. Em seguida, colocam os revestimentos à DIREITA / INFERIOR do "conteúdo muito amplo / alto" (veja a figura abaixo).

==> Portanto, para obter a LARGURA DO CONTEÚDO REAL em alguns navegadores, é necessário subtrair AMBOS os preenchimentos da largura de rolagem e, em alguns navegadores, é necessário apenas subtrair o preenchimento ESQUERDO.

Encontrei uma solução para isso e queria adicionar isso como um comentário, mas não era permitido. Então tirei a foto e a deixei um pouco mais clara em relação aos "paddings movidos" e à "scrollWidth não confiável". Na ÁREA AZUL, você encontra minha solução sobre como obter a LARGURA "REAL" do CONTEÚDO!

Espero que isso ajude a tornar as coisas ainda mais claras!

insira a descrição da imagem aqui

Manny_user3297459
fonte
13

Há um bom artigo sobre o MDN que explica a teoria por trás desses conceitos: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements

Ele também explica as importantes diferenças conceituais entre a largura / altura do boundingClientRect e o offsetWidth / offsetHeight.

Então, para provar a teoria certa ou errada, você precisa de alguns testes. Foi o que fiz aqui: https://github.com/lingtalfi/dimensions-cheatsheet

Está testando para chrome53, ff49, safari9, edge13 e ie11.

Os resultados dos testes provam que a teoria geralmente está correta. Para os testes, criei 3 divs contendo 10 parágrafos de lorem ipsum cada. Algum css foi aplicado a eles:

.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);
}

E aqui estão os resultados:

  • div1

    • offsetWidth: 530 (chrome53, ff49, safari9, edge13, ie11)
    • offsetHeight: 330 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.width: 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.width: 500 (chrome53, ff49, safari9, edge13, ie11)
    • bcr.height: 300 (chrome53, ff49, safari9)
    • altura de bcr: 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.width: 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)

Portanto, além do valor da altura do boundingClientRect (299.9999694824219 em vez do esperado 300) em edge13 e ie11, os resultados confirmam que a teoria por trás disso funciona.

A partir daí, aqui está minha definição desses conceitos:

  • offsetWidth / offsetHeight: dimensões da caixa de borda do layout
  • boundingClientRect: dimensões da caixa de borda de renderização
  • clientWidth / clientHeight: dimensões da parte visível da caixa de preenchimento do layout (excluindo barras de rolagem)
  • scrollWidth / scrollHeight: dimensões da caixa de preenchimento do layout, se não foi restringida pelas barras de rolagem

Nota: a largura da barra de rolagem vertical padrão é 12px em edge13, 15px em chrome53, ff49 e safari9 e 17px em ie11 (feita por medidas no photoshop a partir de capturas de tela e comprovada pelos resultados dos testes).

No entanto, em alguns casos, talvez seu aplicativo não esteja usando a largura da barra de rolagem vertical padrão.

Portanto, dadas as definições desses conceitos, a largura da barra de rolagem vertical deve ser igual a (no pseudo-código):

  • dimensão do layout: offsetWidth - clientWidth - (borderLeftWidth + borderRightWidth)

  • dimensão de renderização: boundingClientRect.width - clientWidth - (borderLeftWidth + borderRightWidth)

Observe que, se você não entender o layout versus a renderização, leia o artigo mdn.

Além disso, se você tiver outro navegador (ou se quiser ver os resultados dos testes por conta própria), poderá ver minha página de teste aqui: http://codepen.io/lingtalfi/pen/BLdBdL

ling
fonte