Na auditoria do Lampyx, 107 dos 108 posts tinham pelo menos uma imagem com alt="" no corpo do artigo. As imagens destacadas estavam certas. O problema era a imagem dentro do texto, e a biblioteca de mídia já tinha o texto alternativo de cada arquivo. O alt estava guardado, mas não chegava na tag do post.
Abrir 107 posts um por um seria repetitivo demais. Fiz por script e conferi nas páginas publicadas. O código está abaixo.
Por que isso importa
O Google usa o texto alternativo, junto com o conteúdo da página, para entender do que se trata a imagem. Leitores de tela leem esse texto para quem não enxerga a imagem. A recomendação do Google é escrever uma descrição útil e que combine com o assunto da página, sem encher de palavra-chave. O exemplo dele: alt="Dalmatian puppy playing fetch" é melhor que alt="puppy", e uma imagem sem alt é o exemplo ruim.
Como achei o problema
Li o corpo de cada post e contei as tags <img> com alt vazio. Nos meus posts, o editor de blocos grava a imagem assim (aqui como estava, com o alt vazio):
<img src="..." alt="" class="wp-image-619"/>
O número depois de wp-image- é o ID do arquivo na biblioteca de mídia. É por ele que dá para buscar o alt que já existe.
O script
Roda no console do navegador (F12), numa página do painel de administração, com você logado como administrador. A autenticação por cookie da API do WordPress exige um nonce no cabeçalho X-WP-Nonce. Sem ele a API trata a requisição como visitante e recusa a edição.
const nonce = (await (await fetch('/wp-admin/admin-ajax.php?action=rest-nonce')).text()).trim();
const api = (caminho, opcoes = {}) =>
fetch('/wp-json/wp/v2/' + caminho, {
...opcoes,
headers: { 'X-WP-Nonce': nonce, 'Content-Type': 'application/json' },
}).then(r => r.json());
const esc = s => s.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
function preencherAlt(html, altPorId) {
return html.replace(/<img\b[^>]*>/g, tag => {
const m = tag.match(/wp-image-(\d+)/);
if (!m || !altPorId[m[1]] || !/\salt=""/.test(tag)) return tag;
return tag.replace(/\salt=""/, ` alt="${esc(altPorId[m[1]])}"`);
});
}
async function corrigirPost(id, aplicar = false) {
const post = await api(`posts/${id}?context=edit&_fields=id,content`);
const html = post.content.raw;
const ids = new Set();
for (const tag of html.match(/<img\b[^>]*>/g) ?? []) {
const m = tag.match(/wp-image-(\d+)/);
if (m && /\salt=""/.test(tag)) ids.add(m[1]);
}
const altPorId = {};
for (const imgId of ids) {
altPorId[imgId] = (await api(`media/${imgId}?_fields=id,alt_text`)).alt_text;
}
const novo = preencherAlt(html, altPorId);
if (novo === html) return false;
if (aplicar) {
await api(`posts/${id}`, { method: 'POST', body: JSON.stringify({ content: novo }) });
}
return true;
}
Primeiro rode com aplicar = false, que só conta quantos posts mudariam e não grava nada:
let lista = [];
for (const pg of [1, 2]) {
const r = await api(`posts?per_page=100&page=${pg}&_fields=id`);
if (Array.isArray(r)) lista = lista.concat(r);
}
let mudaria = 0;
for (const p of lista) if (await corrigirPost(p.id)) mudaria++;
console.log(lista.length, 'posts,', mudaria, 'mudariam');
Se o número bater com o esperado, troque para corrigirPost(p.id, true) e rode de novo.
Como validei que só o alt mudou
Antes de gravar, comparei o HTML novo com o antigo depois de tirar todos os atributos alt dos dois. Se os dois ficam iguais, a única diferença é o alt. Em código:
const semAlt = s => s.replace(/\salt="[^"]*"/g, '');
semAlt(novo) === semAlt(html); // tem que ser true
O WordPress guarda revisões de cada post. Se algo sair errado, dá para voltar para a versão anterior pelo editor.
Como confirmei no ar
Depois da gravação e da limpeza do cache, baixei cada uma das 108 páginas como visitante e contei as imagens com alt vazio dentro do conteúdo do artigo. Deu zero. Rodei a versão sem gravação do script de novo e ela também retornou zero posts a corrigir.
O que o script não resolve
- Usei o texto que já estava na biblioteca de mídia. Eu não reescrevi nenhum. Se o texto de lá é ruim, o alt do post fica ruim. Leia os principais e melhore à mão.
- Imagem sem o atributo
altna tag, e não só vazio, não entra nesse script. O meu caso eraalt="". - Imagem que não tem o ID no
class(colada de fora do editor) também fica de fora. - Imagem que só enfeita e não informa nada pode ficar com alt vazio de propósito. Aqui eram imagens com conteúdo, então o vazio era erro.
O que fazer agora
- Rode a contagem com
aplicar = falseno seu site. - Leia uma amostra dos textos da biblioteca de mídia antes de aplicar.
- Aplique, limpe o cache e confira as páginas como visitante.
- Daqui para frente, preencha o alt no upload. É uma linha por imagem.
Eu fiz essa correção junto com a configuração do Yoast, que contei em Yoast SEO: a configuração que usei em 108 posts.
Fontes: Google Search Central, boas práticas para imagens e WordPress, autenticação da API REST.