How to

Kako napraviti gumb koji ne mijenja veličinu tijekom loading-a?

Naučite kako spriječiti pomake u izgledu gumba tijekom loading state-a koristeći grid-stacking ili apsolutno pozicioniranje uz stručne uvide AS Agency-a.

Kako napraviti gumb koji ne mijenja veličinu tijekom loading-a?

Ključne riječi:

tailwind
nextjs
frontend

Problem 💡

Jeste li se ikada susreli sa situacijom gdje trebate implementirati loading state za gumb, ali nakon klika gumb mijenja veličinu?

blog image

Slika: Gumb koji mijenja velicinu u loading state-u

Ovo ponašanje može izgledati nespretno, neprofesionalno i neprijateljski prema korisniku. Također uvodi pomake u izgledu, što negativno utječe na korisničko iskustvo vaše web stranice i SEO izvedbu. Kako to spriječiti? Kako pristupiti ovom problemu?

Postoje dva glavna pristupa za rješavanje ovog problema, ali jedan se ističe kao bolji. Istražimo oba.

  1. Absolute positioning pristup
  2. Grid stacking pristup

Početni pristup

U početku biste mogli pomisliti prikazati tekst prema zadanim postavkama i, kada je loading state aktivan, zamijeniti tekst spinnerom. Evo kako možete kreirati osnovnu komponentu gumba:

1import Spinner from './spinner';
2
3type TProps = {
4  text: string;
5  isLoading?: boolean;
6  onClick: () => void;
7};
8const Button = ({ text, isLoading, onClick }: TProps) => {
9  return (
10    <button
11      onClick={onClick}
12      className='px-4 py-2 bg-purple-500 text-white flex justify-between items-center'
13    >
14      {!isLoading ? <span>{text}</span> : <Spinner />}
15    </button>
16  );
17};
18
19export default Button;
20

Kao što vidite, ovo je jednostavna komponenta gumba s isLoading svojstvom koje određuje hoće li biti prikazan tekst ili spinner.

1'use client';
2
3import Button from './components/button';
4import { useState } from 'react';
5
6export default function Home() {
7  const [isLoading, setIsLoading] = useState(false);
8  
9  const handleLoading = () => {
10    setIsLoading(true);
11    setTimeout(() => {
12      setIsLoading(false);
13    }, 3000);
14  };
15  
16  return (
17    <main className='grid place-items-center h-screen'>
18      <Button
19        text='Submit this form'
20        onClick={() => handleLoading()}
21        isLoading={isLoading}
22      />
23    </main>
24  );
25}
26

Evo kako biste prikazali komponentu gumba.

Međutim, ovaj pristup dovodi do problema s promjenom veličine: kada se prikazuje tekst, gumb ima veću veličinu, a kada se aktivira loading state, gumb postaje uži. Ovdje trebamo ponovno razmotriti rješenje.

1. Absolute Positioning pristup 📊

Kako biste riješili problem promjene veličine, možete koristiti apsolutno pozicioniranje. Ovo zahtijeva nekoliko prilagodbi unutar komponenti gumba i spinnera.

1import Spinner from './spinner';
2
3type TProps = {
4  text: string;
5  isLoading?: boolean;
6  onClick: () => void;
7};
8const Button = ({ text, isLoading, onClick }: TProps) => {
9  return (
10    <button
11      onClick={onClick}
12      className='px-4 py-2 bg-purple-500 text-white flex justify-between items-center relative'
13    >
14      <span className={`${isLoading ? 'invisible' : 'visible'}`}>{text}</span>
15      {isLoading && (
16        <div className='absolute inset-0 w-full flex items-center justify-center'>
17          <Spinner />
18        </div>
19      )}
20    </button>
21  );
22};
23
24export default Button;
25

Promjene uključuju dodavanje klase relative gumbu, izmjenu načina prikazivanja teksta gumba tijekom loading state i omatanje komponente Spinner dodatnim spremnikom.

Ali u čemu je nedostatak ovog pristupa? Iako rješava problem promjene veličine, uvodi novi problem - preljev teksta.

blog image

Slika: Problem sa absolute pristupom

Kao što je ovdje prikazano, ako je poruka loading state dulja od početnog teksta gumba, dolazi do prelijevanja teksta. Za rješavanje ovog problema potrebno je bolje rješenje.

2. Grid Stacking pristup 🌐

Grid stacking pruža elegantan i robustan način rješavanja ovog problema. Kod ovog pristupa sve promjene potrebne su unutar same komponente gumba.

1import Spinner from './spinner';
2
3type TProps = {
4  text: string;
5  isLoading?: boolean;
6  onClick: () => void;
7};
8const Button = ({ text, isLoading, onClick }: TProps) => {
9  return (
10    <button
11      onClick={onClick}
12      className={`px-4 py-2 bg-purple-500 text-white grid [grid-template-areas:stack]`}
13    >
14      <span
15        className={`[grid-area:stack] ${!isLoading ? 'visible' : 'invisible'}`}
16      >
17        {text}
18      </span>
19      <Spinner
20        className={`[grid-area:stack] place-self-center ${
21          isLoading ? 'visible' : 'invisible'
22        }`}
23      />
24    </button>
25  );
26};
27
28export default Button;
29

Prvo, trebate dodati grid kao className gumbu, čime omogućavate CSS Grid izgled. Ključni className ovdje je [grid-template-areas:stack], koji definira jedno grid područje nazvano stack. Ovo osigurava da se svi child elementi slažu jedan na drugi.

Kako bi grid-područja radila, primijenite [grid-area:stack] na tekst gumba i komponentu Spinner. Dodatno, koristite className poput visible i invisible za upravljanje vidljivošću elemenata prema potrebi.

blog image

Slika: Grid stacking pristup output

Izlaz spinnera ostaje identičan pristupu s apsolutnim pozicioniranjem. Međutim, grid stacking nudi značajnu prednost.

blog image

Slika: Grid stacking approach sa dužom loading porukom

Kao što je prikazano, grid pristup uzima u obzir najveći child element prilikom određivanja veličine gumba. To znači da početna veličina gumba već uključuje dulju poruku loading state, sprječavajući pomake u izgledu.

Zaključak 🔧

Ovaj blog post detaljno opisuje dva pristupa za rješavanje problema promjene veličine gumba tijekom loading state: apsolutno pozicioniranje i grid stacking. Iako apsolutno pozicioniranje nudi brzo rješenje, grid stacking se pokazuje kao superiornije rješenje jer izbjegava preljev teksta i pomake u izgledu, čime poboljšava korisničko iskustvo i održava SEO izvedbu.

U AS Agency, mi pažljivo pristupamo ovim nijansama kako bismo osigurali da web stranice naših klijenata budu dotjerane, prilagođene korisnicima i optimizirane za najbolju izvedbu. Ako tražite tim koji prioritet daje svakom detalju kako bi isporučio izvanredne rezultate, ne tražite dalje. Stvorimo nešto nevjerojatno zajedno! ✨