Sto scrivendo un’app Svelte che deve installarsi in ogni pagina. Ho capito come aggiungere il mio file JS nella sezione head e farlo caricare al primo caricamento della pagina. Tuttavia, mentre sperimentavo, mi sono reso conto che discourse carica nuovi contenuti tramite XHR e sostituisce determinate sezioni, quindi la mia app non veniva re-inizializzata quando veniva caricata una nuova pagina.
Ho provato vari tentativi per essere avvisato quando la pagina cambia, ma sembra che Ember non fornisca gli hook e non sono riuscito a trovare alcun evento personalizzato a cui potessi ascoltare.
Sembra che un modo sia aggiungere un osservatore di mutazioni al DOM e monitorare le modifiche. Ho scoperto che il `#topic div sembra essere quello che viene ricaricato (e gli attributi cambiano in modo da poter semplicemente monitorare le modifiche degli attributi). Ho impostato un osservatore di mutazioni lì (gli osservatori di mutazioni sono il modo performante più recente per monitorare le modifiche del DOM). Il modo in cui funziona è monitorare le modifiche e, quando si verificano, eseguire una callback per ricaricare la mia app Svelte per quella pagina.
Mi piace questo approccio e vorrei un feedback.
Una domanda: dovrei invece monitorare le modifiche all’URL? È una buona idea registrare un listener per popstate?
Per usarlo, fai qualcosa di simile in questo tuo tema/head:
<script src="https://files.extrastatic.dev/community/on-discourse.js"></script>
<script src="https://files.extrastatic.dev/community/index.js"></script>
<link rel="stylesheet" type="text/css" href="https://files.extrastatic.dev/community/index.css">
Quindi, all’interno della tua libreria puoi chiamare on-discourse in questo modo:
function log(...msg) {
console.log('svelte', ...msg);
}
// Questo è il codice di installazione di Svelte
function setup() {
try {
const ID = 'my-special-target-id';
log('Inside setup()');
const el = document.getElementById(ID);
if (el) {
log('Removed existing element', ID);
el.remove();
}
const target = document.createElement("div");
target.setAttribute("id", ID);
log('Created target');
document.body.appendChild(target);
log('Appended child to body');
const app = new App({
// eslint-disable-next-line no-undef
target
});
log('Created app and installed');
} catch(err) {
console.error('Unable to complete setup()', err.toString() );
}
}
(function start() {
log('Starting custom Svelte app');
// Re-install on changes
window.onDiscourse && window.onDiscourse( setup );
// Load the app the first page load
window.addEventListener('load', () => {
setup();
});
log('Finished custom Svelte app);
})();
In sostanza, basta chiamare window.onDiscourse(callback) con la tua callback (e puoi eseguirla più volte per installare più callback), e poi quando si verifica una mutazione, quella callback viene eseguita per inizializzare la tua app.
Ecco il codice completo per on-discourse.js. (edit: ho aggiornato questo per usare #topic, che sembra una buona cosa da monitorare, perché gli attributi cambiano quando la pagina si carica, e poi la mutazione deve solo monitorare le modifiche degli attributi, piuttosto che cercare nell’intero albero DOM per #main-outlet)
let mutationObservers = [];
function log(...msg) {
console.log("on-discourse", ...msg);
}
function observeMainOutlet() {
log('Observing main outlet');
// Select the node that will be observed for mutations
const targetNode = document.getElementById("topic");
if (targetNode) {
// Options for the observer (which mutations to observe)
const config = { attributes: true };
// create an observer instance to reset when childList changes
const observer = new MutationObserver(function(mutations) {
let reset = false;
mutations.forEach(function(mutation) {
if (mutation.type === 'attributes') {
log('Found main-outlet mutation, running callbacks');
mutationObservers.forEach( (s,i) => {
try {
log(`Running div callback ${i+1}`);
s();
log(`Finished div callback ${i+1}`);
} catch( err ) {
log(`Div callback error (${i+1})`, err );
}
});
}
});
});
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
// Later, you can stop observing
// observer.disconnect();
log('Done with outlet observer');
} else {
console.error('on-discourse FATAL ERROR: Unable to find main-outlet');
}
}
window.addDiscourseDivMutationObserver = (cb) => {
log('Adding on-discourse div mutation callback');
mutationObservers.push(cb);
log('Added on-discourse div mutation callback');
}
window.addEventListener("load", () => {
log('Setting up topic observer');
if (mutationObservers.length > 0) {
observeMainOutlet();
}
log('Created topic observer');
});
log('Completed setup of on-discourse.js');