Je développe une application Svelte qui doit s’installer sur chaque page. J’ai trouvé comment ajouter mon fichier JS dans la section head et le faire charger au premier chargement de la page. Cependant, en expérimentant, j’ai réalisé que Discourse chargeait du nouveau contenu via XHR et remplaçait certaines sections, et donc mon application n’était pas réinitialisée lorsqu’une nouvelle page se chargeait.
J’ai essayé diverses tentatives pour être notifié lorsque la page change, mais il semble qu’Ember ne fournisse pas les hooks, et je n’ai pas réussi à trouver d’événements personnalisés auxquels je pourrais m’abonner.
Il semble qu’une façon soit d’ajouter un observateur de mutations au DOM et de surveiller les changements. J’ai trouvé que le `#topic div semble être celui qui est rechargé (et les attributs changent, vous pouvez donc simplement surveiller les changements d’attributs). J’ai configuré un observateur de mutations là-bas (les observateurs de mutations sont la nouvelle méthode performante pour surveiller les changements DOM). La façon dont cela fonctionne est de surveiller les changements, et lorsqu’ils se produisent, d’exécuter une fonction de rappel pour recharger mon application Svelte pour cette page.
J’aime cette approche et j’aimerais avoir votre avis.
Une question : devrais-je plutôt surveiller les changements d’URL ? Est-ce une meilleure idée d’enregistrer un écouteur pour popstate ?
Pour l’utiliser, faites quelque chose comme ceci dans votre thème/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">
Ensuite, à l’intérieur de votre bibliothèque, vous pouvez appeler on-discourse comme ceci :
function log(...msg) {
console.log('svelte', ...msg);
}
// Ceci est le code d'installation 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');
// Réinstaller en cas de changements
window.onDiscourse && window.onDiscourse( setup );
// Charger l'application au premier chargement de la page
window.addEventListener('load', () => {
setup();
});
log('Finished custom Svelte app);
})();
En gros, vous appelez simplement window.onDiscourse(callback) avec votre fonction de rappel (et vous pouvez l’exécuter plusieurs fois pour installer plusieurs fonctions de rappel), et lorsqu’une mutation se produit, cette fonction de rappel est exécutée pour initialiser votre application.
Voici le code complet pour on-discourse.js. (edit : j’ai mis à jour ceci pour utiliser #topic, ce qui semble être une bonne chose à surveiller, car les attributs changent lorsque la page se charge, et la mutation n’a alors qu’à surveiller les changements d’attributs, plutôt que de parcourir l’intégralité de l’arborescence DOM pour #main-outlet)
let mutationObservers = [];
function log(...msg) {
console.log("on-discourse", ...msg);
}
function observeMainOutlet() {
log('Observing main outlet');
// Sélectionner le nœud qui sera observé pour les mutations
const targetNode = document.getElementById("topic");
if (targetNode) {
// Options pour l'observateur (quelles mutations observer)
const config = { attributes: true };
// créer une instance d'observateur pour réinitialiser lorsque childList change
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 );
}
});
}
});
});
// Commencer à observer le nœud cible pour les mutations configurées
observer.observe(targetNode, config);
// Plus tard, vous pouvez arrêter d'observer
// 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');