One approach would be using a MutationObserver (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to call your course sorting function whenever any new children is added to the ul element.
If the ul element exists on page load, and it's list-items is appended asynchronously, it's rather painless (and not very expensive in terms of performance):
(() => {
const ul = document.querySelector( 'ul.course-list' );
// If we can't find our target ul, bail.
if ( ! ul ) {
return;
}
const sortCourses = () => {
// Add the code you already have for sorting the courses here.
};
if (ul.querySelector("li")) {
sortCourses();
}
const config = { childList: true };
const observer = new MutationObserver(() => {
observer.disconnect();
sortCourses();
observer.observe(ul, config);
});
observer.observe(ul, config);
})();
https://codesandbox.io/s/black-waterfall-w2eh19?file=/src/course-sort.js
If, on the other hand, the ul element itself is also added to the dom as an effect of the async request, it'll be a bit more tricky.
Start by finding the closest parent to your target ul, that exists when the page loads.
Preferably, it would be a wrapper that will have no other injected elements aside from the course list (you could in theory use body, but the callback would be called extremely often, negatively impacting the site performance)
In this example, we'll be setting up two MutationObservers.
One that will await the ul to exist (which will be terminated once it does), and the other to watch for changes to the ul childList.
(() => {
// Closest parent that exists on page load.
const wrapper = document.querySelector("#my-courses");
// Selector where we will be able to find our target ul once it exists.
const ulSelector = "ul.course-list";
// If wrapper could not be found, bail.
if (!wrapper) return;
/**
* Listen for children being added or removed
*/
const observeUl = () => {
const ul = document.querySelector(ulSelector);
const sortCourses = () => {
// The code you already have for sorting the list-items.
};
const observerConfig = { childList: true };
// Listen for added or removed children of our target ul.
const observer = new MutationObserver(() => {
// Stop listening (since sorting involves removing and adding elements)
observer.disconnect();
// Sort the courses.
sortCourses();
// Start listening again
observer.observe(ul, observerConfig);
});
observer.observe(ul, observerConfig);
return true;
};
/**
* @param {String} selector Selector to look for.
* @param {Function} callback Called once the selector returns a match.
*/
const waitForSelector = (selector, callback) => {
/**
* First check if the selector already exists.
* If it does, call the callback and exit this function.
*/
if (document.querySelector(selector)) {
callback();
return;
}
/**
* In addition to listening to changes to the targets childList,
* We'll watch the childList of the target and it's children recursively.
*/
const config = { childList: true, subtree: true };
const observer = new MutationObserver(() => {
if (wrapper.querySelector(ulSelector)) {
// ul exists! Let's disconnect this observer and call the callback.
observer.disconnect();
callback();
}
});
observer.observe(wrapper, config);
};
// Wait for the ul to exist, and then start observing it.
waitForSelector(ulSelector, observeUl);
})();
https://codesandbox.io/s/naughty-saha-tzmxlw?file=/src/course-sort.js