All articles
How toDec 11, 2024 · 7 min read

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.

Antonio ŠumakFounder, AS Agency
Kako napraviti gumb koji ne mijenja veličinu tijekom loading-a?

Problem 💡

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

Gumb koji mijenja velicinu u loading state-u
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:

TypeScript
import Spinner from './spinner';

type TProps = {
  text: string;
  isLoading?: boolean;
  onClick: () => void;
};
const Button = ({ text, isLoading, onClick }: TProps) => {
  return (
    <button
      onClick={onClick}
      className='px-4 py-2 bg-purple-500 text-white flex justify-between items-center'
    >
      {!isLoading ? <span>{text}</span> : <Spinner />}
    </button>
  );
};

export default Button;

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

TypeScript
'use client';

import Button from './components/button';
import { useState } from 'react';

export default function Home() {
  const [isLoading, setIsLoading] = useState(false);
  
  const handleLoading = () => {
    setIsLoading(true);
    setTimeout(() => {
      setIsLoading(false);
    }, 3000);
  };
  
  return (
    <main className='grid place-items-center h-screen'>
      <Button
        text='Submit this form'
        onClick={() => handleLoading()}
        isLoading={isLoading}
      />
    </main>
  );
}

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.

TypeScript
import Spinner from './spinner';

type TProps = {
  text: string;
  isLoading?: boolean;
  onClick: () => void;
};
const Button = ({ text, isLoading, onClick }: TProps) => {
  return (
    <button
      onClick={onClick}
      className='px-4 py-2 bg-purple-500 text-white flex justify-between items-center relative'
    >
      <span className={`${isLoading ? 'invisible' : 'visible'}`}>{text}</span>
      {isLoading && (
        <div className='absolute inset-0 w-full flex items-center justify-center'>
          <Spinner />
        </div>
      )}
    </button>
  );
};

export default Button;

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.

Problem sa absolute pristupom
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.

TypeScript
import Spinner from './spinner';

type TProps = {
  text: string;
  isLoading?: boolean;
  onClick: () => void;
};
const Button = ({ text, isLoading, onClick }: TProps) => {
  return (
    <button
      onClick={onClick}
      className={`px-4 py-2 bg-purple-500 text-white grid [grid-template-areas:stack]`}
    >
      <span
        className={`[grid-area:stack] ${!isLoading ? 'visible' : 'invisible'}`}
      >
        {text}
      </span>
      <Spinner
        className={`[grid-area:stack] place-self-center ${
          isLoading ? 'visible' : 'invisible'
        }`}
      />
    </button>
  );
};

export default Button;

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.

Grid stacking pristup output
Grid stacking pristup output

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

Grid stacking approach sa dužom loading porukom
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! ✨

Written by Antonio Šumak

Founder of AS Agency. I design and build fast websites for small businesses — and write down what I learn along the way.

Share

Booking Oct / Nov 2026 · 2 spots left

Ready to show the worldyour business?

Let’s talk about your idea on a free, friendly 30-minute call. You’ll leave with a clear plan and a fair quote.

Zagreb--:--New York--:--