Design & UX

25 ago, 2015

Contando elementos com nth-last-child

Publicidade

Acredito que não há dúvidas de que o design responsivo veio pra ficar e que não está mais apenas na moda. Mais do que ficar pensando em tamanhos específicos de telas ou em mobile e tablet, minha opinião é que esqueçamos isso. Com a variedade de smartphones diferentes que existem hoje, acho difícil separarmos o que é mobile do que é tablet ou desktop. Um exemplo disso é o Viewport sizes, que traz uma lista completa com os tamanhos dos viewports de vários dispositivos diferentes. Mas isso é só a minha opinião.

Acredito que o principal é fazer com que o seu conteúdo, independente de resolução ou tela, seja acessível pro usuário; afinal, e muitas vezes esquecemos disso, é exatamente isso que importa: que o usuário encontre a informação que procura. Quem aqui nunca entrou em algum site para procurar algo e por um motivo xis não conseguiu acessar aquela informação, seja por ela não estar disposta de forma correta, ou por algum problema cross-browser e por aí vai…

Adaptando seu conteúdo

Pra mim, uma das principais vertentes que o design responsivo trouxe foi a priorização de volta no conteúdo. Mais que qualquer coisa, devemos focar na informação que deve ser passada e garantir que ela seja acessada independente de dispositivo ou resolução.

E nesse caminho das pedras, uma hora ou outra teremos que trabalhar com um tipo de conteúdo que pode variar. E aí, como fazer? Nesses dias cai numa situação desse tipo e vi que há diversos caminhos para resolver esse problema.

O problema do conteúdo variável

Jogo rápido: imaginem uma lista disposta horizontalmente com 5 itens. Podemos pensar então que em uma determinada resolução em que cada item teria 20% de largura da lista e em um determinado breakpoint cada item ficaria com a largura total da lista (ou se formos pelo lado do mobile first, cada item teria por padrão a largura inteira da lista e, só a partir de um determinado breakpoint que ele ficaria com a nova largura de 20%).

.list li {
  width: 20%;
  float: left;
}

@media screen and (max-width: 768px) {
  width: 100%;
  float: none;
}

Ou, se pensarmos numa abordagem mobile first (apenas a encargo de exemplo):

@media screen and (min-width: 768px) {
  width: 20%;
  float: left;
}

Beleza, matou! Mas, e se a quantidade de itens variar? Por exemplo entre 3 e 5? Me deparei com uma situação semelhante alguns dias atrás e meu primeiro pensamento foi: antes da renderização dos elementos na página eu vejo quantos itens tem ali e, dependendo do resultado, coloco uma classe na lista. Algo mais ou menos assim:

.list li {
  float: left;
}

.list-3 li { width: 33.3%; }
.list-4 li { width: 25%; }
.list-5 li { width: 20%; }

Resolve o problema? Resolve. Mas nem sempre temos essa opção, de contar por exemplo com o lado do servidor para nos retornar o número de itens. Aí nesse caso, uma outra solução seria via JavaScript, seguindo o mesmo pensamento: contar os elementos e de acordo com o resultado, aplicar uma classe na lista.

E tem ainda uma outra coisa: nesse caso, nem estamos pensando ainda em breakpoints e variações de resolução. Paramos ainda no primeiro ponto que é simplesmente que o seu conteúdo esteja bem disposto numa resolução qualquer; para depois disso, podermos pensar na estratégia a ser seguida.

Dando uma pesquisada na Barsa da Internet, vi que daria para resolver esse problema apenas com o velho e bom CSS.

Contando os elementos

A ideia básica é, através do CSS, descobrirmos quantos itens estão presentes na lista e a partir daí aplicarmos a largura correta para cada item. Mas, como contar? Podemos usar a magia negra dos pseudo seletores pra nos ajudar com isso.

O cara nth-last-child

Esse brother aí, nada mais faz do que contar o elemento partindo do seu último item. Então, se quiséssemos, por exemplo, selecionar o segundo item da lista, mas iniciando a contagem do fim, faríamos algo assim:

.list li:nth-last-child(2) {
  border: solid 1px red;
}

Uma representação bem simples do item selecionado:

  • item 1 (selecionado)
  • item 2 (selecionado)
  • item 3 (selecionado)
  • item 4 (selecionado)
  • item 5

Tendo ideia de como selecionar um elemento, podemos estabelecer a seguinte linha de raciocínio:

  • temos que saber exatamente quantos itens temos, chegando no primeiro elemento da lista.
  • selecionar todos os elementos irmãos.

Vamos por parte… Conseguimos matar o primeiro item utilizando o que vimos no exemplo anterior. Então, partindo de uma lista de 5 itens, poderíamos fazer algo assim:

.list li:nth-last-child(5),
.list li:nth-last-child(5) ~ li {
  width: 20%;
  // \o/
}

E shaaazam! Conseguimos teoricamente contar quantos elementos temos na lista. Agora é só aplicarmos as demais variações.

list li:nth-last-child(3),
.list li:nth-last-child(3) ~ li { width: 33.3%; }

.list li:nth-last-child(4),
.list li:nth-last-child(4) ~ li { width: 25%; }

.list li:nth-last-child(5),
.list li:nth-last-child(5) ~ li { width: 20%; }

Fiz um exemplo rápido no CodePen com o que falamos aqui.

HTML

<ul class="list">
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
<ul class="list">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
</ul>
<ul class="list">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>

CSS

* { box-sizing: border-box; }
.list {
  background-color: #f5f5f5;
  height: 50px;
  margin: 10px 0;
  list-style-type: none;
  padding: 0;
}

.list li {
  background-color: #bbb;
  color: #fff;
  text-align: center;
  height: 50px;
  float: left;
  line-height: 40px;
  font-size: 24px;
  border: solid 5px #aaa;
}

.list li:nth-last-child(3),
.list li:nth-last-child(3) ~ li { width: 33.3%; }

.list li:nth-last-child(4),
.list li:nth-last-child(4) ~ li { width: 25%; }

.list li:nth-last-child(5),
.list li:nth-last-child(5) ~ li { width: 20%; }

RESULT

result

Algumas referências e links legais:

Gostou? Escrevi alguma groselha? Quer melhorar? Abra uma issue mencionando o post e vamos conversar.

***

Artigo originalmente publicado em: http://www.raphaelfabeni.com.br/contando-elementos-nth-last-child/