Ordenação de Eventos
O aprendiz organiza eventos, etapas ou itens em ordem cronológica ou lógica. Avalia compreensão de processos, causas e efeitos e progressão temporal.
Experimente
Clique nos eventos na ordem cronológica em que aconteceram:
Selecione ou arraste os eventos. Você pode remover e reorganizar antes de verificar:
Sua sequência:
A sequência ainda está vazia.
Código-fonte
Dados de exemplo
Três eventos embaralhados, cada um com a posição correta em data-ordem.
| 1 |
<?php
|
| 2 |
$eventos = [
|
| 3 |
['id' => 'e1', 'ordem' => 1, 'texto' => 'O pedido é feito pelo aplicativo.'],
|
| 4 |
['id' => 'e2', 'ordem' => 2, 'texto' => 'O restaurante prepara e embala a comida.'],
|
| 5 |
['id' => 'e3', 'ordem' => 3, 'texto' => 'O entregador leva o pedido até a porta.'],
|
| 6 |
];
|
| 7 |
$embaralhados = $eventos;
|
| 8 |
shuffle($embaralhados);
|
| 9 |
?>
|
| 10 |
|
Eventos disponíveis e sequência
Uma lista de origem e uma lista de destino — a sequência nasce vazia, com aviso próprio.
| 1 |
<div class="questao" data-tipo="ordenacao-eventos">
|
| 2 |
|
| 3 |
<p class="questao-instrucao">Clique ou arraste os eventos na ordem correta:</p>
|
| 4 |
|
| 5 |
<div class="eventos-disponiveis">
|
| 6 |
<?php foreach ($embaralhados as $e): ?>
|
| 7 |
<button type="button" class="evento-item" draggable="true" data-id="<?= $e['id'] ?>" data-ordem="<?= $e['ordem'] ?>">
|
| 8 |
<?= htmlspecialchars($e['texto']) ?>
|
| 9 |
</button>
|
| 10 |
<?php endforeach; ?>
|
| 11 |
</div>
|
| 12 |
|
| 13 |
<p class="questao-rotulo">Sua sequência:</p>
|
| 14 |
<div class="sequencia-lista" role="list">
|
| 15 |
<p class="sequencia-vazia">A sequência ainda está vazia.</p>
|
| 16 |
</div>
|
| 17 |
<template class="sequencia-template">
|
| 18 |
<div class="sequencia-item" draggable="true" tabindex="0">
|
| 19 |
<span class="sequencia-numero"></span>
|
| 20 |
<span class="sequencia-texto"></span>
|
| 21 |
<div class="sequencia-acoes">
|
| 22 |
<button type="button" class="sequencia-subir" aria-label="Mover para cima">↑</button>
|
| 23 |
<button type="button" class="sequencia-descer" aria-label="Mover para baixo">↓</button>
|
| 24 |
<button type="button" class="sequencia-remover" aria-label="Remover da sequência">×</button>
|
| 25 |
</div>
|
| 26 |
</div>
|
| 27 |
</template>
|
| 28 |
|
| 29 |
<div class="questao-acoes">
|
| 30 |
<button type="button" class="questao-verificar" hidden>Verificar ordem</button>
|
| 31 |
<p class="questao-feedback" hidden></p>
|
| 32 |
<button type="button" class="questao-reset" hidden>Tentar novamente</button>
|
| 33 |
</div>
|
| 34 |
|
| 35 |
</div>
|
Card e listas
Contêiner geral e as duas listas — disponíveis e sequência.
| 1 |
.questao[data-tipo="ordenacao-eventos"] {
|
| 2 |
max-width: 460px;
|
| 3 |
padding: 24px;
|
| 4 |
border: 1px solid #e2e8f0;
|
| 5 |
border-radius: 16px;
|
| 6 |
background: #fff;
|
| 7 |
font-family: sans-serif;
|
| 8 |
}
|
| 9 |
|
| 10 |
.questao-instrucao { margin: 0 0 16px; font-weight: 600; color: #0f172a; }
|
| 11 |
.questao-rotulo { margin: 16px 0 8px; font-size: 12px; font-weight: 700; text-transform: uppercase; color: #64748b; }
|
| 12 |
|
| 13 |
.eventos-disponiveis {
|
| 14 |
display: flex;
|
| 15 |
flex-direction: column;
|
| 16 |
gap: 8px;
|
| 17 |
}
|
| 18 |
|
| 19 |
.evento-item {
|
| 20 |
padding: 12px 16px;
|
| 21 |
border: 1px solid #cbd5e1;
|
| 22 |
border-radius: 10px;
|
| 23 |
background: #f8fafc;
|
| 24 |
text-align: left;
|
| 25 |
font: inherit;
|
| 26 |
cursor: grab;
|
| 27 |
}
|
Sequência e indicador de inserção
Caixa tracejada quando vazia; a borda lateral mostra onde o item vai entrar ao arrastar.
| 1 |
.sequencia-lista {
|
| 2 |
min-height: 64px;
|
| 3 |
padding: 8px;
|
| 4 |
border: 2px dashed #cbd5e1;
|
| 5 |
border-radius: 12px;
|
| 6 |
background: rgba(241, 245, 249, .5);
|
| 7 |
}
|
| 8 |
|
| 9 |
.sequencia-vazia { margin: 12px; text-align: center; font-size: 13px; color: #64748b; }
|
| 10 |
|
| 11 |
.sequencia-item {
|
| 12 |
position: relative;
|
| 13 |
display: grid;
|
| 14 |
grid-template-columns: 24px minmax(0, 1fr) auto;
|
| 15 |
align-items: center;
|
| 16 |
gap: 10px;
|
| 17 |
padding: 10px 12px;
|
| 18 |
margin-bottom: 6px;
|
| 19 |
border: 1px solid #cbd5e1;
|
| 20 |
border-radius: 10px;
|
| 21 |
background: #fff;
|
| 22 |
font-size: 13px;
|
| 23 |
cursor: grab;
|
| 24 |
}
|
| 25 |
|
| 26 |
.sequencia-item[data-insercao="antes"] { border-top: 3px solid #6366f1; }
|
| 27 |
.sequencia-item[data-insercao="depois"] { border-bottom: 3px solid #6366f1; }
|
| 28 |
|
| 29 |
.sequencia-numero {
|
| 30 |
display: inline-flex;
|
| 31 |
align-items: center;
|
| 32 |
justify-content: center;
|
| 33 |
width: 24px;
|
| 34 |
height: 24px;
|
| 35 |
border-radius: 999px;
|
| 36 |
background: rgba(99, 102, 241, .15);
|
| 37 |
color: #4338ca;
|
| 38 |
font-weight: 700;
|
| 39 |
}
|
Botões de ação
Reordenar/remover no card, e verificar/reiniciar no fim do card.
| 1 |
.sequencia-acoes { display: flex; gap: 4px; }
|
| 2 |
.sequencia-acoes button {
|
| 3 |
width: 28px;
|
| 4 |
height: 28px;
|
| 5 |
border: none;
|
| 6 |
border-radius: 6px;
|
| 7 |
background: #e2e8f0;
|
| 8 |
cursor: pointer;
|
| 9 |
}
|
| 10 |
.sequencia-acoes button:disabled { opacity: .4; cursor: not-allowed; }
|
| 11 |
.sequencia-remover { color: #b91c1c; }
|
| 12 |
|
| 13 |
.questao-acoes { margin-top: 16px; display: flex; flex-direction: column; gap: 10px; }
|
| 14 |
|
| 15 |
.questao-verificar,
|
| 16 |
.questao-reset {
|
| 17 |
padding: 12px 16px;
|
| 18 |
border: none;
|
| 19 |
border-radius: 10px;
|
| 20 |
background: #4f46e5;
|
| 21 |
color: #fff;
|
| 22 |
font: inherit;
|
| 23 |
font-weight: 600;
|
| 24 |
cursor: pointer;
|
| 25 |
}
|
| 26 |
|
| 27 |
.questao-reset { background: #e5e7eb; color: #1f2937; }
|
| 28 |
|
| 29 |
.questao-feedback { padding: 10px 14px; border-radius: 10px; font-size: 14px; }
|
| 30 |
.questao-feedback.acerto { background: #dcfce7; color: #14532d; }
|
| 31 |
.questao-feedback.erro { background: #fee2e2; color: #7f1d1d; }
|
Encontrar os elementos da atividade
As duas listas, o molde de card e a área de ação, compartilhados por toda a interação.
| 1 |
document.addEventListener('DOMContentLoaded', function () {
|
| 2 |
document.querySelectorAll('.questao[data-tipo="ordenacao-eventos"]').forEach(function (questao) {
|
| 3 |
var disponiveis = questao.querySelector('.eventos-disponiveis');
|
| 4 |
var seqLista = questao.querySelector('.sequencia-lista');
|
| 5 |
var template = questao.querySelector('.sequencia-template');
|
| 6 |
var verificar = questao.querySelector('.questao-verificar');
|
| 7 |
var reset = questao.querySelector('.questao-reset');
|
| 8 |
var feedback = questao.querySelector('.questao-feedback');
|
| 9 |
var itens = questao.querySelectorAll('.evento-item');
|
| 10 |
var arrastadoDisponivel = null;
|
| 11 |
var arrastadoSequencia = null;
|
| 12 |
var bloqueada = false;
|
| 13 |
|
| 14 |
function cards() { return Array.prototype.slice.call(seqLista.querySelectorAll('.sequencia-item')); }
|
Atualizar números e disponibilidade dos controles
Reflete a ordem atual nos cards e só libera "Verificar" com a sequência completa.
| 1 |
function atualizar() {
|
| 2 |
var atuais = cards();
|
| 3 |
var vazio = seqLista.querySelector('.sequencia-vazia');
|
| 4 |
if (vazio) vazio.hidden = atuais.length > 0;
|
| 5 |
atuais.forEach(function (card, indice) {
|
| 6 |
card.querySelector('.sequencia-numero').textContent = indice + 1;
|
| 7 |
card.querySelector('.sequencia-subir').disabled = bloqueada || indice === 0;
|
| 8 |
card.querySelector('.sequencia-descer').disabled = bloqueada || indice === atuais.length - 1;
|
| 9 |
card.querySelector('.sequencia-remover').disabled = bloqueada;
|
| 10 |
});
|
| 11 |
verificar.hidden = bloqueada || atuais.length !== itens.length;
|
| 12 |
}
|
Remover e reordenar
Remover devolve o evento à lista de origem; mover troca a posição do card.
| 1 |
function origemDo(card) {
|
| 2 |
return questao.querySelector('.evento-item[data-id="' + card.dataset.id + '"]');
|
| 3 |
}
|
| 4 |
|
| 5 |
function remover(card) {
|
| 6 |
if (bloqueada) return;
|
| 7 |
var origem = origemDo(card);
|
| 8 |
if (origem) origem.hidden = false;
|
| 9 |
card.remove();
|
| 10 |
atualizar();
|
| 11 |
}
|
| 12 |
|
| 13 |
function mover(card, delta) {
|
| 14 |
if (bloqueada) return;
|
| 15 |
var atuais = cards();
|
| 16 |
var indice = atuais.indexOf(card);
|
| 17 |
var destino = indice + delta;
|
| 18 |
if (destino < 0 || destino >= atuais.length) return;
|
| 19 |
if (delta < 0) seqLista.insertBefore(card, atuais[destino]);
|
| 20 |
else seqLista.insertBefore(atuais[destino], card);
|
| 21 |
atualizar();
|
| 22 |
}
|
Criar um card na sequência
Clona o molde, liga os botões e o arraste, e esconde o evento de origem.
| 1 |
function criarCard(item) {
|
| 2 |
if (bloqueada || item.hidden) return null;
|
| 3 |
var fragmento = template.content.cloneNode(true);
|
| 4 |
var card = fragmento.querySelector('.sequencia-item');
|
| 5 |
card.dataset.id = item.dataset.id;
|
| 6 |
card.dataset.ordem = item.dataset.ordem;
|
| 7 |
card.querySelector('.sequencia-texto').textContent = item.textContent.trim();
|
| 8 |
card.querySelector('.sequencia-subir').addEventListener('click', function () { mover(card, -1); });
|
| 9 |
card.querySelector('.sequencia-descer').addEventListener('click', function () { mover(card, 1); });
|
| 10 |
card.querySelector('.sequencia-remover').addEventListener('click', function () { remover(card); });
|
| 11 |
|
| 12 |
// Arraste dentro da sequência: a borda lateral (CSS via data-insercao)
|
| 13 |
// mostra se o solto entra antes ou depois deste card. bloqueada tem
|
| 14 |
// que ser checado aqui, e não só nos botões ↑/↓/× — sem isso, dá pra
|
| 15 |
// reordenar por arraste mesmo depois de travar a sequência avaliada.
|
| 16 |
card.addEventListener('dragstart', function (evento) {
|
| 17 |
if (bloqueada) { evento.preventDefault(); return; }
|
| 18 |
arrastadoSequencia = card;
|
| 19 |
});
|
| 20 |
card.addEventListener('dragend', function () { delete card.dataset.insercao; arrastadoSequencia = null; });
|
| 21 |
card.addEventListener('dragover', function (evento) {
|
| 22 |
if (!arrastadoSequencia && !arrastadoDisponivel) return;
|
| 23 |
evento.preventDefault();
|
| 24 |
var caixa = card.getBoundingClientRect();
|
| 25 |
card.dataset.insercao = evento.clientY < caixa.top + caixa.height / 2 ? 'antes' : 'depois';
|
| 26 |
});
|
| 27 |
card.addEventListener('drop', function (evento) {
|
| 28 |
evento.preventDefault();
|
| 29 |
var antes = card.dataset.insercao === 'antes';
|
| 30 |
var inserido = arrastadoSequencia || criarCard(arrastadoDisponivel);
|
| 31 |
if (inserido) seqLista.insertBefore(inserido, antes ? card : card.nextSibling);
|
| 32 |
delete card.dataset.insercao;
|
| 33 |
arrastadoDisponivel = null; arrastadoSequencia = null;
|
| 34 |
atualizar();
|
| 35 |
});
|
| 36 |
|
| 37 |
item.hidden = true;
|
| 38 |
return card;
|
| 39 |
}
|
| 40 |
|
| 41 |
function adicionar(item) {
|
| 42 |
var card = criarCard(item);
|
| 43 |
if (!card) return;
|
| 44 |
seqLista.appendChild(card);
|
| 45 |
atualizar();
|
| 46 |
}
|
Adicionar por clique ou arraste
Clique é o atalho de um único gesto; arrastar solta em qualquer ponto da lista.
| 1 |
itens.forEach(function (item) {
|
| 2 |
item.addEventListener('click', function () { adicionar(item); });
|
| 3 |
item.addEventListener('dragstart', function (evento) {
|
| 4 |
if (bloqueada) { evento.preventDefault(); return; }
|
| 5 |
arrastadoDisponivel = item;
|
| 6 |
});
|
| 7 |
item.addEventListener('dragend', function () { arrastadoDisponivel = null; });
|
| 8 |
});
|
| 9 |
|
| 10 |
seqLista.addEventListener('dragover', function (evento) {
|
| 11 |
if (arrastadoDisponivel || arrastadoSequencia) evento.preventDefault();
|
| 12 |
});
|
| 13 |
seqLista.addEventListener('drop', function (evento) {
|
| 14 |
if (evento.target.closest('.sequencia-item')) return;
|
| 15 |
evento.preventDefault();
|
| 16 |
if (arrastadoDisponivel) adicionar(arrastadoDisponivel);
|
| 17 |
else if (arrastadoSequencia) { seqLista.appendChild(arrastadoSequencia); atualizar(); }
|
| 18 |
arrastadoDisponivel = null; arrastadoSequencia = null;
|
| 19 |
});
|
| 20 |
|
| 21 |
disponiveis.addEventListener('dragover', function (evento) {
|
| 22 |
if (arrastadoSequencia) evento.preventDefault();
|
| 23 |
});
|
| 24 |
disponiveis.addEventListener('drop', function (evento) {
|
| 25 |
evento.preventDefault();
|
| 26 |
if (arrastadoSequencia) remover(arrastadoSequencia);
|
| 27 |
arrastadoSequencia = null;
|
| 28 |
});
|
Verificar e reiniciar
Compara a ordem dos cards com data-ordem; travar impede editar a tentativa já avaliada.
| 1 |
verificar.addEventListener('click', function () {
|
| 2 |
var correta = cards().every(function (card, indice) { return parseInt(card.dataset.ordem, 10) === indice + 1; });
|
| 3 |
feedback.className = 'questao-feedback ' + (correta ? 'acerto' : 'erro');
|
| 4 |
feedback.textContent = correta ? 'Correto! A ordem está certa.' : 'A ordem não está certa.';
|
| 5 |
feedback.hidden = false;
|
| 6 |
bloqueada = true;
|
| 7 |
atualizar();
|
| 8 |
reset.hidden = false;
|
| 9 |
});
|
| 10 |
|
| 11 |
reset.addEventListener('click', function () {
|
| 12 |
bloqueada = false;
|
| 13 |
cards().forEach(function (card) { card.remove(); });
|
| 14 |
itens.forEach(function (item) { disponiveis.appendChild(item); item.hidden = false; });
|
| 15 |
feedback.hidden = true;
|
| 16 |
reset.hidden = true;
|
| 17 |
atualizar();
|
| 18 |
});
|
| 19 |
|
| 20 |
atualizar();
|