Ao final desta aula, você será capaz de:
O CSS e o JavaScript, quando combinados, formam uma poderosa ferramenta para criar experiências de usuário dinâmicas, interativas e envolventes. Enquanto o CSS define a apresentação visual, o JavaScript permite manipular esses estilos em resposta às ações do usuário ou a eventos do sistema.
Nesta aula, exploraremos como:
A regra de ouro é: use CSS para estilização e animações simples, e JavaScript para lógica e interatividade complexa. Quando ambos trabalham juntos, você aproveita o melhor dos dois mundos.
Existem três maneiras principais de manipular estilos CSS através de JavaScript:
Você pode acessar e modificar diretamente as propriedades CSS de um elemento através do objeto style
.
Clique aqui para mudar o estilo diretamente
// Manipulando o estilo diretamente
const element = document.getElementById('styleDemo');
element.addEventListener('click', () => {
// Propriedades CSS são acessadas em camelCase
element.style.backgroundColor = '#6d28d9';
element.style.color = 'white';
element.style.padding = '20px';
element.style.borderRadius = '10px';
element.style.transition = 'all 0.3s ease';
});
Manipular diretamente a propriedade style
:
backgroundColor
em vez de background-color
)Uma abordagem mais flexível e organizada é adicionar, remover ou alternar classes CSS predefinidas.
Clique aqui para alternar classes CSS
// Manipulando classes
const element = document.getElementById('classDemo');
element.addEventListener('click', () => {
// Alternar uma classe
element.classList.toggle('active');
// Adicionar uma classe
// element.classList.add('highlight');
// Remover uma classe
// element.classList.remove('highlight');
// Verificar se uma classe existe
// if (element.classList.contains('active')) { ... }
});
Manipular classes CSS é geralmente a abordagem preferida porque:
Você pode obter os estilos computados de um elemento, independentemente de como esses estilos foram definidos.
// Obtendo estilos computados
const element = document.getElementById('someElement');
const styles = window.getComputedStyle(element);
console.log(styles.backgroundColor); // rgb(255, 255, 255)
console.log(styles.fontSize); // 16px
// Você pode usar isso para tomar decisões com base no estilo atual
if (styles.display === 'none') {
// O elemento está oculto
}
As variáveis CSS (Custom Properties) fornecem uma maneira poderosa de criar interfaces dinâmicas, pois podem ser manipuladas em tempo real com JavaScript.
Este container usa variáveis CSS para definir cores e espaçamentos.
/* CSS com variáveis */
.theme-dark {
--bg-color: #1f2937;
--text-color: #f9fafb;
--border-color: #374151;
--highlight-color: #5b21b6;
}
.theme-light {
--bg-color: #f9fafb;
--text-color: #1f2937;
--border-color: #e5e7eb;
--highlight-color: #8b5cf6;
}
.theme-container {
background-color: var(--bg-color);
color: var(--text-color);
border: 1px solid var(--border-color);
}
/* JavaScript para alternar o tema */
const themeContainer = document.getElementById('themeDemo');
const themeButton = document.getElementById('toggleTheme');
themeButton.addEventListener('click', () => {
themeContainer.classList.toggle('theme-dark');
themeContainer.classList.toggle('theme-light');
});
Você também pode definir e obter variáveis CSS diretamente:
// Definindo uma variável CSS
document.documentElement.style.setProperty('--highlight-color', '#9333ea');
// Obtendo o valor de uma variável CSS
const highlightColor = getComputedStyle(document.documentElement)
.getPropertyValue('--highlight-color');
As animações CSS são poderosas por si só, mas quando combinadas com JavaScript, oferecem controle granular e possibilidades criativas expandidas.
A forma mais simples de controlar animações é adicionar ou remover classes que contêm as definições de animação.
/* CSS com animações */
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
@keyframes slideIn {
from { transform: translateX(-100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.bounce { animation: bounce 1s infinite; }
.slide-in { animation: slideIn 0.5s forwards; }
/* JavaScript para controlar a animação */
document.getElementById('animBounce').addEventListener('click', () => {
const target = document.getElementById('animTarget');
target.classList.remove('slide-in');
target.classList.add('bounce');
});
document.getElementById('animSlide').addEventListener('click', () => {
const target = document.getElementById('animTarget');
target.classList.remove('bounce');
target.classList.add('slide-in');
});
document.getElementById('animStop').addEventListener('click', () => {
const target = document.getElementById('animTarget');
target.classList.remove('bounce', 'slide-in');
});
Você pode controlar detalhes de animação e sincronizar com eventos ou lógica de aplicativo.
// Eventos de animação
const element = document.querySelector('.animated-element');
element.addEventListener('animationstart', () => {
console.log('Animação iniciada');
});
element.addEventListener('animationend', () => {
console.log('Animação concluída');
// Execute ações após o término da animação
element.classList.add('post-animation-state');
});
element.addEventListener('animationiteration', () => {
console.log('Ciclo de animação concluído');
});
A API de Animação da Web oferece controle total sobre animações diretamente no JavaScript.
// Criando uma animação com Web Animation API
const element = document.querySelector('.animation-target');
// Definindo keyframes
const keyframes = [
{ transform: 'translateX(0) scale(1)', backgroundColor: '#6d28d9' },
{ transform: 'translateX(100px) scale(1.2)', backgroundColor: '#9333ea' },
{ transform: 'translateX(0) scale(1)', backgroundColor: '#6d28d9' }
];
// Definindo opções de tempo
const options = {
duration: 2000,
iterations: Infinity,
easing: 'ease-in-out'
};
// Criando e iniciando a animação
const animation = element.animate(keyframes, options);
// Controlando a animação
document.getElementById('pauseBtn').addEventListener('click', () => animation.pause());
document.getElementById('playBtn').addEventListener('click', () => animation.play());
document.getElementById('reverseBtn').addEventListener('click', () => animation.reverse());
Animações com CSS | Animações com JavaScript |
---|---|
Melhor performance para animações simples | Necessário para animações baseadas em lógica complexa |
Declarativas e fáceis de implementar | Maior controle e flexibilidade |
Limitadas a keyframes predefinidos | Podem reagir a eventos e dados em tempo real |
Boas para transições de estado | Boas para animações interativas ou baseadas em física |
Combinando CSS e JavaScript, podemos criar interações complexas e responsivas.
Implementação de uma funcionalidade simples de arrastar e soltar:
/* CSS para drag and drop */
.draggable {
padding: 1rem;
background-color: #6d28d9;
color: white;
cursor: move;
transition: transform 0.2s;
}
.draggable.dragging {
opacity: 0.5;
transform: scale(1.05);
}
.drop-zone {
min-height: 100px;
border: 2px dashed #6d28d9;
padding: 1rem;
}
.drop-zone.drag-over {
background-color: rgba(109, 40, 217, 0.1);
}
/* JavaScript para drag and drop */
const draggables = document.querySelectorAll('.draggable');
const dropZones = document.querySelectorAll('.drop-zone');
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging');
});
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging');
});
});
dropZones.forEach(zone => {
zone.addEventListener('dragover', e => {
e.preventDefault();
zone.classList.add('drag-over');
});
zone.addEventListener('dragleave', () => {
zone.classList.remove('drag-over');
});
zone.addEventListener('drop', e => {
e.preventDefault();
zone.classList.remove('drag-over');
const dragging = document.querySelector('.dragging');
if (dragging) {
zone.appendChild(dragging);
}
});
});
Use a Intersection Observer API para criar efeitos baseados na posição de rolagem:
Role para ver os itens aparecerem:
/* CSS para elementos que vão animar no scroll */
.scroll-item {
padding: 2rem;
margin: 2rem 0;
background-color: #f3f4f6;
transform: translateX(-100px);
opacity: 0;
transition: all 0.5s ease-out;
}
.scroll-item.visible {
transform: translateX(0);
opacity: 1;
}
/* JavaScript com IntersectionObserver */
const scrollItems = document.querySelectorAll('.scroll-item');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
} else {
// Opcional: remover a classe quando o item sair da visualização
// entry.target.classList.remove('visible');
}
});
}, {
threshold: 0.1 // Porcentagem do item visível necessária para disparar
});
scrollItems.forEach(item => {
observer.observe(item);
});
Várias APIs modernas melhoram a integração entre CSS e JavaScript:
Monitora mudanças no tamanho de elementos, útil para ajustes de layout responsivos sem usar media queries.
// Monitorando o tamanho de um elemento
const element = document.querySelector('.resize-observe');
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
// Adaptar estilos com base no tamanho
if (width < 500) {
entry.target.classList.add('compact');
} else {
entry.target.classList.remove('compact');
}
}
});
resizeObserver.observe(element);
Observa mudanças no DOM, incluindo atributos como classes CSS.
// Monitorando mudanças em classes CSS
const element = document.querySelector('.observe-changes');
const observer = new MutationObserver(mutations => {
for (let mutation of mutations) {
if (mutation.attributeName === 'class') {
const currentClasses = mutation.target.className;
console.log('Classes alteradas:', currentClasses);
// Reagir a classes específicas
if (currentClasses.includes('active')) {
// Fazer algo quando a classe 'active' for adicionada
}
}
}
});
// Configurar o que observar
observer.observe(element, { attributes: true });
Parte das APIs Houdini, fornece uma interface orientada a objetos para trabalhar com valores CSS.
// Usando Typed OM para manipulações de estilo mais rápidas e tipadas
const elem = document.querySelector('.typed-om-example');
// Abordagem tradicional
elem.style.opacity = 0.5;
// Abordagem com Typed OM
elem.attributeStyleMap.set('opacity', 0.5);
// Trabalhar com unidades
elem.attributeStyleMap.set('font-size', CSS.px(16));
elem.attributeStyleMap.set('padding', CSS.px(20));
// Animações matemáticas - combinar valores
const paddingAnimation = elem.attributeStyleMap.get('padding').value + 10;
elem.attributeStyleMap.set('padding', CSS.px(paddingAnimation));
Ao trabalhar com APIs modernas, sempre verifique o suporte do navegador e forneça fallbacks quando necessário.
// Verificando suporte a uma API antes de usá-la
if ('IntersectionObserver' in window) {
// Usar IntersectionObserver
} else {
// Fallback para scroll event listener
}
Existem muitas bibliotecas que facilitam a integração entre CSS e JavaScript:
Biblioteca | Descrição | Casos de Uso |
---|---|---|
GSAP | Biblioteca robusta de animação com amplo suporte de navegador | Animações complexas, sequências, controle de linha do tempo |
Animate.css | Coleção de animações CSS prontas para usar | Animações simples controladas por classes |
Motion One | API moderna para animação baseada na Web Animations API | Alternativa leve ao GSAP com sintaxe moderna |
Framer Motion | Biblioteca de animação para React | Animações em aplicativos React |
AOS | Animate On Scroll | Efeitos de rolagem simples sem código JavaScript personalizado |
Popmotion | Biblioteca funcional de animação | Animações baseadas em física, gestos |
Sortable.js | Biblioteca de drag-and-drop | Listas ordenáveis e arrastar e soltar |
// Exemplo com GSAP
import { gsap } from "gsap";
// Animação simples
gsap.to(".box", {
duration: 1,
x: 100,
rotation: 360,
ease: "bounce"
});
// Timeline para sequências
const tl = gsap.timeline();
tl.to(".box1", { duration: 0.5, x: 100 })
.to(".box2", { duration: 0.5, y: 50 }, "-=0.25")
.to(".box3", { duration: 0.5, scale: 1.2 });
// Exemplo de minimização de reflows
// Ruim: Causa múltiplos reflows
element.style.width = '100px';
console.log(element.offsetWidth); // Força reflow
element.style.height = '100px'; // Outro reflow
element.style.margin = '10px'; // Mais um reflow
// Bom: Agrupa mudanças
// Leituras
const width = element.offsetWidth;
const height = element.offsetHeight;
// Então escritas (todas de uma vez)
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';
// Ou use classes
element.classList.add('new-dimensions');
Implemente um sistema de temas claro/escuro utilizando variáveis CSS e JavaScript:
Crie um menu responsivo (hambúrguer) com animações suaves:
Dica: Use CSS para as animações e JavaScript apenas para alternar classes.
Implemente uma galeria de imagens/cartões que reage à posição de rolagem:
Escolha e implemente uma das seguintes interfaces interativas:
Dica: Combine todas as técnicas vistas nesta aula, mas priorize a experiência do usuário e a acessibilidade.
Agora que você entende como CSS e JavaScript trabalham juntos, pode explorar:
Na próxima aula, exploraremos metodologias CSS que ajudam a organizar e escalar estilos em projetos grandes!