Mapa Conceitual
Representação visual das relações entre conceitos. O aprendiz conecta ideias com setas e rótulos que descrevem a relação entre elas, revelando sua compreensão da estrutura do conhecimento.
Experimente
Hábitos e Saúde
Os 4 conceitos abaixo se influenciam. Descubra as 4 relações que faltam.
Cada relação tem três partes: um conceito, uma palavra que liga, outro conceito. Monte as suas no formulário mais abaixo, como no exemplo.
A palavra do meio é o que vale: melhora, prejudica e depende de dizem coisas muito diferentes sobre o mesmo par de conceitos.
Exemplo
Suas relações
Nenhuma ainda — monte a primeira abaixo.
Monte sua relação
Código-fonte
Conceitos e vocabulário
As duas listas fechadas da atividade. O vocabulário de relações existe porque o formato pressupõe que esses termos foram ensinados antes — sem ele a pessoa adivinha que palavra usar.
| 1 |
$conceitos = [
|
| 2 |
'c1' => 'Evaporação',
|
| 3 |
'c2' => 'Condensação',
|
| 4 |
'c3' => 'Precipitação',
|
| 5 |
'c4' => 'Vegetação',
|
| 6 |
];
|
| 7 |
|
| 8 |
$relacoes = [
|
| 9 |
'r1' => 'origina',
|
| 10 |
'r2' => 'depende de',
|
| 11 |
'r3' => 'aumenta',
|
| 12 |
];
|
| 13 |
|
Gabarito
Cada par esperado aceita um único rótulo do vocabulário. É isso que torna a correção em certo/errado justa: com sinônimo disponível, ela reprovaria uma leitura correta.
| 1 |
$esperadas = [
|
| 2 |
['de' => 'c1', 'rotulo' => 'r1', 'para' => 'c2',
|
| 3 |
'porque' => 'O vapor que sobe da superfície é o que forma as nuvens ao esfriar.'],
|
| 4 |
['de' => 'c2', 'rotulo' => 'r1', 'para' => 'c3',
|
| 5 |
'porque' => 'Quando as gotículas da nuvem ficam pesadas demais, elas caem como chuva.'],
|
| 6 |
['de' => 'c4', 'rotulo' => 'r3', 'para' => 'c1',
|
| 7 |
'porque' => 'As folhas liberam vapor de água, somando à evaporação do solo e dos rios.'],
|
| 8 |
];
|
| 9 |
|
| 10 |
// Uma relação já pronta, para o formato ser reconhecível sem interagir.
|
| 11 |
$exemplo = ['de' => 'c3', 'rotulo' => 'r1', 'para' => 'c4'];
|
| 12 |
?>
|
| 13 |
|
Conceitos disponíveis
Os conceitos aparecem antes do formulário: a pessoa precisa ver o conjunto inteiro para decidir o que se liga a quê.
| 1 |
<div class="mapa" data-esperadas="<?= htmlspecialchars(json_encode($esperadas), ENT_QUOTES) ?>">
|
| 2 |
<p class="mapa-enunciado">
|
| 3 |
Os <?= count($conceitos) ?> conceitos abaixo se influenciam.
|
| 4 |
Descubra as <?= count($esperadas) ?> relações que faltam.
|
| 5 |
</p>
|
| 6 |
|
| 7 |
<div class="mapa-conceitos">
|
| 8 |
<?php foreach ($conceitos as $id => $texto): ?>
|
| 9 |
<div class="mapa-conceito"><?= htmlspecialchars($texto) ?></div>
|
| 10 |
<?php endforeach; ?>
|
| 11 |
</div>
|
| 12 |
|
Exemplo pronto
Só UMA relação vem montada. Entregar o mapa fechado exibiria o produto do trabalho e não sobraria o que fazer.
| 1 |
<p class="mapa-rotulo">Exemplo</p>
|
| 2 |
<div class="mapa-relacao" data-estado="exemplo">
|
| 3 |
<span class="mapa-no"><?= htmlspecialchars($conceitos[$exemplo['de']]) ?></span>
|
| 4 |
<span class="mapa-seta">—</span>
|
| 5 |
<span class="mapa-rotulo-relacao"><?= htmlspecialchars($relacoes[$exemplo['rotulo']]) ?></span>
|
| 6 |
<span class="mapa-seta">→</span>
|
| 7 |
<span class="mapa-no"><?= htmlspecialchars($conceitos[$exemplo['para']]) ?></span>
|
| 8 |
</div>
|
Lista da pessoa
Nasce vazia e cresce a cada relação montada; o aviso de vazio some na primeira adição.
| 1 |
<p class="mapa-rotulo">Suas relações</p>
|
| 2 |
<div class="mapa-lista"></div>
|
| 3 |
<p class="mapa-vazio">Nenhuma ainda — monte a primeira abaixo.</p>
|
Montador da relação
Três <select> em vez de campos de texto: a escolha vem das listas fechadas, então o rótulo é obrigatório por construção e não há resposta impossível de corrigir.
| 1 |
<div class="mapa-montador">
|
| 2 |
<select class="mapa-de">
|
| 3 |
<option value="">Conceito A…</option>
|
| 4 |
<?php foreach ($conceitos as $id => $texto): ?>
|
| 5 |
<option value="<?= $id ?>"><?= htmlspecialchars($texto) ?></option>
|
| 6 |
<?php endforeach; ?>
|
| 7 |
</select>
|
| 8 |
|
| 9 |
<select class="mapa-relacao-escolha">
|
| 10 |
<option value="">relação…</option>
|
| 11 |
<?php foreach ($relacoes as $id => $texto): ?>
|
| 12 |
<option value="<?= $id ?>"><?= htmlspecialchars($texto) ?></option>
|
| 13 |
<?php endforeach; ?>
|
| 14 |
</select>
|
| 15 |
|
| 16 |
<select class="mapa-para">
|
| 17 |
<option value="">Conceito B…</option>
|
| 18 |
<?php foreach ($conceitos as $id => $texto): ?>
|
| 19 |
<option value="<?= $id ?>"><?= htmlspecialchars($texto) ?></option>
|
| 20 |
<?php endforeach; ?>
|
| 21 |
</select>
|
| 22 |
|
| 23 |
<button type="button" class="mapa-adicionar">Adicionar</button>
|
| 24 |
</div>
|
| 25 |
<p class="mapa-aviso" hidden></p>
|
Verificar e refazer
Ação primária larga, painel de resultado, ação secundária larga nascendo oculta — nessa ordem.
| 1 |
<div class="mapa-acoes">
|
| 2 |
<button type="button" class="mapa-verificar">Verificar relações</button>
|
| 3 |
<div class="mapa-feedback" hidden></div>
|
| 4 |
<button type="button" class="mapa-reiniciar" hidden>Tentar novamente</button>
|
| 5 |
</div>
|
| 6 |
</div>
|
Conceitos do mapa
Os conceitos ficam em destaque acima do formulário — é a lista de onde as escolhas saem.
| 1 |
.mapa-conceitos {
|
| 2 |
display: grid;
|
| 3 |
grid-template-columns: repeat(2, 1fr);
|
| 4 |
gap: 12px;
|
| 5 |
margin-bottom: 20px;
|
| 6 |
}
|
| 7 |
|
| 8 |
.mapa-conceito {
|
| 9 |
padding: 10px 12px;
|
| 10 |
border: 1px solid rgb(99 102 241 / 0.3);
|
| 11 |
border-radius: 12px;
|
| 12 |
background: rgb(99 102 241 / 0.1);
|
| 13 |
color: #4f46e5;
|
| 14 |
font-weight: 600;
|
| 15 |
text-align: center;
|
| 16 |
}
|
A relação montada
Uma linha com as três partes: conceito, rótulo, conceito. O rótulo tem cor própria porque é a parte que carrega o sentido.
| 1 |
.mapa-relacao {
|
| 2 |
display: flex;
|
| 3 |
flex-wrap: wrap;
|
| 4 |
align-items: center;
|
| 5 |
gap: 8px;
|
| 6 |
padding: 8px 12px;
|
| 7 |
border-radius: 8px;
|
| 8 |
border: 1px solid #cbd5e1;
|
| 9 |
background: rgb(241 245 249 / 0.5);
|
| 10 |
font-size: 13px;
|
| 11 |
margin-bottom: 6px;
|
| 12 |
}
|
| 13 |
|
| 14 |
.mapa-no { font-weight: 600; color: #4f46e5; }
|
| 15 |
.mapa-rotulo-relacao { font-style: italic; color: #b45309; }
|
| 16 |
.mapa-seta { color: #64748b; }
|
Estados da correção
Verde para a relação que está no gabarito, vermelho para a que não está. Fundo, borda e cor do texto mudam juntos — só o fundo não basta para quem enxerga cor de forma diferente.
| 1 |
.mapa-relacao[data-estado="acerto"] {
|
| 2 |
border-color: rgb(34 197 94 / 0.5);
|
| 3 |
background: rgb(34 197 94 / 0.12);
|
| 4 |
}
|
| 5 |
|
| 6 |
.mapa-relacao[data-estado="acerto"] .mapa-no,
|
| 7 |
.mapa-relacao[data-estado="acerto"] .mapa-rotulo-relacao { color: #15803d; }
|
| 8 |
|
| 9 |
.mapa-relacao[data-estado="erro"] {
|
| 10 |
border-color: rgb(239 68 68 / 0.5);
|
| 11 |
background: rgb(239 68 68 / 0.12);
|
| 12 |
}
|
| 13 |
|
| 14 |
.mapa-relacao[data-estado="erro"] .mapa-no,
|
| 15 |
.mapa-relacao[data-estado="erro"] .mapa-rotulo-relacao { color: #b91c1c; }
|
Montador
Os três campos empilham em tela estreita: lado a lado eles espremem até sobrar só a seta do select.
| 1 |
.mapa-montador {
|
| 2 |
display: flex;
|
| 3 |
flex-wrap: wrap;
|
| 4 |
gap: 8px;
|
| 5 |
padding: 16px;
|
| 6 |
border: 1px solid #cbd5e1;
|
| 7 |
border-radius: 12px;
|
| 8 |
background: #f1f5f9;
|
| 9 |
}
|
| 10 |
|
| 11 |
.mapa-montador select {
|
| 12 |
flex: 1;
|
| 13 |
min-width: 120px;
|
| 14 |
padding: 8px 12px;
|
| 15 |
border: 1px solid #cbd5e1;
|
| 16 |
border-radius: 8px;
|
| 17 |
font-size: 13px;
|
| 18 |
}
|
| 19 |
|
| 20 |
@media (max-width: 640px) {
|
| 21 |
.mapa-montador { flex-direction: column; }
|
| 22 |
.mapa-montador select { width: 100%; }
|
| 23 |
}
|
| 24 |
|
| 25 |
.mapa-aviso { margin-top: 8px; font-size: 13px; color: #b45309; }
|
Painel de resultado
Cada resposta em caixa própria, não em texto corrido: com quatro ou cinco itens, achar onde uma termina depende só de o olho seguir a quebra de linha.
| 1 |
.mapa-acoes {
|
| 2 |
display: flex;
|
| 3 |
flex-direction: column;
|
| 4 |
gap: 12px;
|
| 5 |
margin-top: 20px;
|
| 6 |
}
|
| 7 |
|
| 8 |
.mapa-feedback {
|
| 9 |
padding: 16px;
|
| 10 |
border-radius: 12px;
|
| 11 |
color: #f8fafc;
|
| 12 |
}
|
| 13 |
|
| 14 |
.mapa-feedback[data-estado="correta"] { background: #14532d; }
|
| 15 |
.mapa-feedback[data-estado="parcial"] { background: #78350f; }
|
| 16 |
|
| 17 |
.mapa-feedback-lista {
|
| 18 |
list-style: none;
|
| 19 |
margin: 8px 0 0;
|
| 20 |
padding: 0;
|
| 21 |
display: grid;
|
| 22 |
gap: 6px;
|
| 23 |
}
|
| 24 |
|
| 25 |
.mapa-feedback-item {
|
| 26 |
padding: 6px 10px;
|
| 27 |
border-radius: 6px;
|
| 28 |
background: rgb(255 255 255 / 0.07);
|
| 29 |
border-left: 3px solid rgb(255 255 255 / 0.3);
|
| 30 |
}
|
Motivo de cada resposta
Segunda linha do item, subordinada ao título — responde "por que essa relação era esperada".
| 1 |
.mapa-feedback-porque {
|
| 2 |
display: block;
|
| 3 |
margin-top: 3px;
|
| 4 |
font-size: 0.92em;
|
| 5 |
opacity: 0.85;
|
| 6 |
}
|
Texto a partir do id
A relação guarda ids no dataset e só o texto vai para a tela — assim a correção compara ids e não depende do idioma nem da redação do rótulo.
| 1 |
function textoDe(select, id) {
|
| 2 |
var opcao = select.querySelector('option[value="' + id + '"]');
|
| 3 |
return opcao ? opcao.textContent : id;
|
| 4 |
}
|
| 5 |
|
| 6 |
function frase(de, rotulo, para) {
|
| 7 |
return textoDe(selDe, de) + ' — ' + textoDe(selRot, rotulo) + ' → ' + textoDe(selPara, para);
|
| 8 |
}
|
| 9 |
|
| 10 |
function avisar(texto) {
|
| 11 |
aviso.textContent = texto;
|
| 12 |
aviso.hidden = !texto;
|
| 13 |
}
|
Montar uma relação
Cria a linha com as três partes e o botão de remover. O estado inicial é "montada": ainda não passou pela correção.
| 1 |
function criarRelacao(de, rotulo, para) {
|
| 2 |
var item = document.createElement('div');
|
| 3 |
item.className = 'mapa-relacao';
|
| 4 |
item.dataset.estado = 'montada';
|
| 5 |
item.dataset.de = de;
|
| 6 |
item.dataset.rotulo = rotulo;
|
| 7 |
item.dataset.para = para;
|
| 8 |
|
| 9 |
function parte(classe, texto) {
|
| 10 |
var s = document.createElement('span');
|
| 11 |
s.className = classe;
|
| 12 |
s.textContent = texto;
|
| 13 |
return s;
|
| 14 |
}
|
| 15 |
|
| 16 |
item.appendChild(parte('mapa-no', textoDe(selDe, de)));
|
| 17 |
item.appendChild(parte('mapa-seta', '—'));
|
| 18 |
item.appendChild(parte('mapa-rotulo-relacao', textoDe(selRot, rotulo)));
|
| 19 |
item.appendChild(parte('mapa-seta', '→'));
|
| 20 |
item.appendChild(parte('mapa-no', textoDe(selPara, para)));
|
| 21 |
|
| 22 |
return item;
|
| 23 |
}
|
Adicionar com validação
Recusa campo em branco, conceitos iguais e relação repetida. O mesmo par com OUTRO rótulo entra: é outra leitura, não repetição.
| 1 |
btnAdd.addEventListener('click', function () {
|
| 2 |
var de = selDe.value, rotulo = selRot.value, para = selPara.value;
|
| 3 |
|
| 4 |
if (!de || !rotulo || !para) { avisar('Escolha os dois conceitos e o tipo de relação.'); return; }
|
| 5 |
if (de === para) { avisar('Escolha dois conceitos diferentes.'); return; }
|
| 6 |
|
| 7 |
var repetida = Array.prototype.some.call(lista.children, function (item) {
|
| 8 |
return item.dataset.de === de && item.dataset.rotulo === rotulo && item.dataset.para === para;
|
| 9 |
});
|
| 10 |
if (repetida) { avisar('Essa relação já está na lista.'); return; }
|
| 11 |
|
| 12 |
avisar('');
|
| 13 |
lista.appendChild(criarRelacao(de, rotulo, para));
|
| 14 |
vazio.hidden = true;
|
| 15 |
|
| 16 |
selDe.selectedIndex = 0;
|
| 17 |
selRot.selectedIndex = 0;
|
| 18 |
selPara.selectedIndex = 0;
|
| 19 |
selDe.focus();
|
| 20 |
});
|
Corrigir contra o gabarito
A relação está no gabarito (acerto) ou não está (erro). O binário só é justo porque o vocabulário é pequeno e cada par esperado aceita um único rótulo.
| 1 |
btnVerif.addEventListener('click', function () {
|
| 2 |
var montadas = Array.prototype.slice.call(lista.children);
|
| 3 |
if (!montadas.length) { avisar('Monte pelo menos uma relação antes de verificar.'); return; }
|
| 4 |
avisar('');
|
| 5 |
|
| 6 |
var acertos = 0;
|
| 7 |
|
| 8 |
montadas.forEach(function (item) {
|
| 9 |
var esperada = esperadas.some(function (e) {
|
| 10 |
return e.de === item.dataset.de && e.rotulo === item.dataset.rotulo && e.para === item.dataset.para;
|
| 11 |
});
|
| 12 |
if (esperada) { acertos++; item.dataset.estado = 'acerto'; }
|
| 13 |
else { item.dataset.estado = 'erro'; }
|
| 14 |
});
|
| 15 |
|
| 16 |
var tudo = acertos === esperadas.length && acertos === montadas.length;
|
| 17 |
mostrarFeedback(tudo, acertos, montadas.length);
|
| 18 |
|
| 19 |
selDe.disabled = selRot.disabled = selPara.disabled = btnAdd.disabled = true;
|
| 20 |
btnVerif.hidden = true;
|
| 21 |
btnReset.hidden = false;
|
| 22 |
});
|
Painel de resultado
Além do placar, lista as relações esperadas com o motivo de cada uma — inclusive as que a pessoa não montou, que é onde está o aprendizado.
| 1 |
function mostrarFeedback(tudo, acertos, total) {
|
| 2 |
feedback.textContent = '';
|
| 3 |
feedback.hidden = false;
|
| 4 |
feedback.dataset.estado = tudo ? 'correta' : 'parcial';
|
| 5 |
|
| 6 |
var mensagem = document.createElement('p');
|
| 7 |
mensagem.textContent = tudo
|
| 8 |
? 'Correto! Você encontrou todas as relações esperadas.'
|
| 9 |
: acertos + '/' + esperadas.length + ' corretas · ' + (total - acertos) + ' incorretas.';
|
| 10 |
feedback.appendChild(mensagem);
|
| 11 |
|
| 12 |
var ul = document.createElement('ul');
|
| 13 |
ul.className = 'mapa-feedback-lista';
|
| 14 |
|
| 15 |
esperadas.forEach(function (e) {
|
| 16 |
var li = document.createElement('li');
|
| 17 |
li.className = 'mapa-feedback-item';
|
| 18 |
|
| 19 |
var titulo = document.createElement('span');
|
| 20 |
titulo.textContent = frase(e.de, e.rotulo, e.para);
|
| 21 |
li.appendChild(titulo);
|
| 22 |
|
| 23 |
var porque = document.createElement('span');
|
| 24 |
porque.className = 'mapa-feedback-porque';
|
| 25 |
porque.textContent = e.porque;
|
| 26 |
li.appendChild(porque);
|
| 27 |
|
| 28 |
ul.appendChild(li);
|
| 29 |
});
|
| 30 |
|
| 31 |
feedback.appendChild(ul);
|
| 32 |
}
|
Refazer
Limpa a lista inteira e destrava os campos. O estado visual nunca precisa ser reconstruído porque ele vive no CSS, atrelado ao data-estado.
| 1 |
btnReset.addEventListener('click', function () {
|
| 2 |
lista.innerHTML = '';
|
| 3 |
vazio.hidden = false;
|
| 4 |
feedback.hidden = true;
|
| 5 |
avisar('');
|
| 6 |
|
| 7 |
selDe.disabled = selRot.disabled = selPara.disabled = btnAdd.disabled = false;
|
| 8 |
selDe.selectedIndex = 0;
|
| 9 |
selRot.selectedIndex = 0;
|
| 10 |
selPara.selectedIndex = 0;
|
| 11 |
|
| 12 |
btnReset.hidden = true;
|
| 13 |
btnVerif.hidden = false;
|
| 14 |
});
|