Un'azione con un clic che copia qualsiasi pagina come Markdown pulito
Leggi qualcosa che vale la pena conservare e lo vuoi nei tuoi appunti. Copiare e incollare dal browser trascina dentro il menu di navigazione, tre annunci, una barra dei cookie e un groviglio di stili inline. Cio che vuoi davvero e Markdown pulito: il titolo, i paragrafi, i link, i blocchi di codice — nient'altro. Questo articolo costruisce un pulsante azione di JustZix che fa esattamente questo, con un clic.
Perche Markdown, e perche un pulsante azione
Markdown e il formato universale per gli appunti — si incolla in modo pulito in Obsidian, Notion, le issue di GitHub, il tuo editor, un file di testo semplice. L'obiettivo qui e un pulsante nella barra delle azioni di JustZix che converte in Markdown o la selezione di testo corrente o l'intero articolo e lo deposita nei tuoi appunti. Niente DevTools, nessuna app extra, nessuna pulizia manuale.
Un'azione di tipo BUTTON e perfetta per questo: mostra un pulsante etichettato nella barra delle azioni, e cliccandolo esegue il tuo JavaScript. Scriveremo ora quel JavaScript.
La strategia di conversione
Percorriamo il DOM ricorsivamente. Per ogni nodo decidiamo: e testo (emettilo), o un elemento che sappiamo convertire (emetti Markdown per esso), o qualcosa da saltare (nav, script, annunci). Tutto cio che non riconosciamo, ci ricorsiamo dentro — cosi i wrapper sconosciuti non perdono il loro contenuto. Quel comportamento di ricorsione predefinita e il ripiego al testo semplice.
// Elements we never want in the output
const SKIP = new Set([
'SCRIPT', 'STYLE', 'NAV', 'HEADER', 'FOOTER', 'ASIDE',
'NOSCRIPT', 'IFRAME', 'FORM', 'BUTTON', 'SVG'
]);
function isHidden(el) {
const s = getComputedStyle(el);
return s.display === 'none' || s.visibility === 'hidden';
}
Convertire gli elementi inline
La conversione inline gestisce la formattazione del testo corrente: grassetto, corsivo, codice, link. Restituisce una stringa, ricorrendo nei figli cosi i tag inline annidati funzionano.
function inline(node) {
if (node.nodeType === Node.TEXT_NODE) {
return node.textContent.replace(/\s+/g, ' ');
}
if (node.nodeType !== Node.ELEMENT_NODE) return '';
if (SKIP.has(node.tagName) || isHidden(node)) return '';
const inner = [...node.childNodes].map(inline).join('');
switch (node.tagName) {
case 'STRONG': case 'B': return '**' + inner.trim() + '**';
case 'EM': case 'I': return '*' + inner.trim() + '*';
case 'CODE': return '`' + inner.trim() + '`';
case 'BR': return ' \n';
case 'A': {
const href = node.getAttribute('href') || '';
const abs = href ? new URL(href, location.href).href : '';
return abs ? '[' + inner.trim() + '](' + abs + ')' : inner;
}
default: return inner;
}
}
Nota la gestione dei link: risolviamo gli href relativi rispetto a location.href con il costruttore URL, cosi un link copiato funziona ancora quando lo incolli altrove.
Convertire gli elementi a blocco
La conversione a blocco gestisce i pezzi strutturali — titoli, paragrafi, elenchi, blocchi di codice, citazioni — e li unisce con righe vuote.
function block(node) {
if (node.nodeType === Node.TEXT_NODE) {
return node.textContent.trim();
}
if (node.nodeType !== Node.ELEMENT_NODE) return '';
if (SKIP.has(node.tagName) || isHidden(node)) return '';
const t = node.tagName;
if (/^H[1-6]$/.test(t)) {
return '#'.repeat(+t[1]) + ' ' + inline(node).trim();
}
if (t === 'P') return inline(node).trim();
if (t === 'BLOCKQUOTE') {
return inline(node).trim()
.split('\n').map(l => '> ' + l).join('\n');
}
if (t === 'PRE') {
return '```\n' + node.textContent.replace(/\n$/, '') + '\n```';
}
if (t === 'UL' || t === 'OL') {
const ordered = t === 'OL';
return [...node.children]
.filter(li => li.tagName === 'LI')
.map((li, i) => (ordered ? (i + 1) + '. ' : '- ')
+ inline(li).trim())
.join('\n');
}
if (t === 'HR') return '---';
if (t === 'IMG') {
const alt = node.getAttribute('alt') || '';
const src = node.src || '';
return src ? '' : '';
}
// Unknown wrapper: recurse, keep the children
return [...node.childNodes].map(block)
.filter(Boolean).join('\n\n');
}
L'ultima riga e il ripiego: un <div>, <section> o <article> per cui non abbiamo una regola speciale ricorre semplicemente nei suoi figli. Nel caso peggiore, un elemento esotico decade al suo testo semplice — mai a nulla.
Prima la selezione, poi l'articolo
Il pulsante dovrebbe essere intelligente: se hai del testo selezionato, converte solo quello; altrimenti converte l'articolo principale. L'API Selection ci da un range che possiamo clonare in un frammento.
function getRoot() {
const sel = window.getSelection();
if (sel && sel.rangeCount && !sel.isCollapsed) {
const frag = sel.getRangeAt(0).cloneContents();
const wrap = document.createElement('div');
wrap.appendChild(frag);
return wrap;
}
// No selection: best guess at the main content
return document.querySelector(
'article, main, [role="main"], .post, .content'
) || document.body;
}
Un frammento di selezione clonato puo iniziare a meta elemento, quindi la conversione a blocco potrebbe vedere nodi parziali — va bene, la nostra regola di ricorsione predefinita la gestisce con grazia.
Collegarlo a un'azione BUTTON e copiare
Ora assembla i pezzi, costruisci la stringa finale e scrivila negli appunti. Questo e il corpo della tua azione BUTTON.
const root = getRoot();
const md = [...root.childNodes]
.map(block)
.filter(Boolean)
.join('\n\n')
.replace(/\n{3,}/g, '\n\n') // collapse extra blank lines
.trim();
navigator.clipboard.writeText(md)
.then(() => JZ.toast('Copied ' + md.length + ' chars of Markdown'))
.catch(() => {
// Fallback for older clipboard restrictions
const ta = document.createElement('textarea');
ta.value = md;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
ta.remove();
JZ.toast('Copied (fallback)');
});
Per trasformare questo in un pulsante: aggiungi un'azione di tipo BUTTON alla regola, etichettala "Copia come Markdown" e incolla il codice sopra come suo gestore. JZ.toast() e l'helper di JustZix per una rapida conferma sulla pagina — utile perche le scritture negli appunti sono altrimenti silenziose.
Rifiniture che vale la pena aggiungere
- Tabelle — aggiungi un caso
TABLEche emette righe delimitate da barre verticali con un separatore---dopo l'intestazione. - Linguaggio del codice — molti siti mettono il linguaggio in una classe come
language-js; leggilo e aggiungilo dopo il triplo apice di apertura. - Front matter — anteponi il titolo della pagina e l'URL come una piccola intestazione cosi sai da dove veniva l'appunto.
- Rimozione delle immagini — se vuoi solo testo, elimina del tutto il caso
IMG.
Perche gli appunti, non un download
Potresti invece attivare il download di un file .md, ma gli appunti vincono per prendere note: clicchi il pulsante, passi alla tua app di appunti, incolli. Nessun file da trovare, nessuna rinomina, nessuna pulizia. La chiamata navigator.clipboard.writeText ha bisogno di un gesto dell'utente — che un clic su un pulsante e, quindi funziona e basta.
Vedi anche
- Un foglio di stile di stampa personalizzato per PDF migliori — un altro modo per estrarre contenuto pulito da una pagina disordinata.
- Un overlay di audit di accessibilita dal vivo — altro JavaScript pratico di attraversamento del DOM.
Un pulsante, Markdown pulito, zero pulizia. Installa JustZix, aggiungi un'azione BUTTON con il codice sopra e inizia a collezionare il web nel modo in cui la tua app di appunti lo vuole davvero.
Valuta questo articolo
Nessuna valutazione — sii il primo.