The way SOUP handles this is by using a jQuery .ajaxComplete handler to detect when new content is loaded dynamically. SOUP actually has a set of wrapper functions that encapsulate this functionality, but here's a simple stand-alone example from this script, which I originally wrote for SOUP but decided to also make available separately:
// re-run the hotfix whenever new posts are loaded via AJAX
var urlRegex = /^\/posts\/(ajax-load-realtime|\d+\/edit-submit)\/|^\/review\/(next-task|task-reviewed)\b/;
$(document).ajaxComplete(function (event, xhr, settings) {
if (urlRegex.test(settings.url)) setTimeout(fixCardLinks, 10);
});
The urlRegex
expression matches all the various AJAX endpoints used by SE to load new posts (including reviews) that I know of.
For requests that load comments rather than posts, you can use the regexp /^\/posts\/((\d+)\/comments|comments\/(\d+))\b/
(where the (\d+)
groups conveniently contain the ID of the post the comments were loaded for).
(The easiest to find these AJAX URLs is probably to use the Network tab in Firefox/Chrome developer tools, although it's also possible to just examine the SE code.)
Based on my testing, the setTimeout()
is actually not needed in most cases, since the .ajaxComplete event fires after the specific callbacks for the AJAX request. It's in the code mostly because I never got around to removing it.
Ps. The script I linked above also hooks into the SE Markdown editor's live preview mechanism. The editor code actually has quite a diverse set of hooks, but for tweaking the preview output, I've found two useful methods:
Method 1 is to use the postConversion
hook, which lets you modify the HTML code generated by the Markdown parser before it's injected into the preview pane. (Trivia: This is how the [tag:foo]
syntax is implemented in the SE code.)
One advantage of this method is that there's no risk of "flickering" in the preview, because you get to modify the HTML before it's displayed. The main disadvantage is that you get the HTML code as plain text, so for anything non-trivial, you'll need to kluge up your own HTML parsing.
Anyway, here's how you define a postConversion
hook, taken straight from the script above:
StackExchange.ifUsing('editor', function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor) {
editor.getConverter().hooks.chain('postConversion', makeMtGLinks);
});
});
The callback function, here named makeMtGLinks
, will be called with the generated HTML as its first parameter, and should return the same HTML code with any modifications you want to make to it. Note that this code will get called pretty often (possibly on every keystroke), so you should try to keep it fairly well optimized.
Method 2 uses the onPreviewRefresh
hook, which is called after the preview pane has been updated. If you'd prefer to make your modifications through the DOM, rather than messing with raw HTML, this is the way to go. The code to define one of these hooks looks like this:
StackExchange.ifUsing('editor', function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
editor.hooks.chain('onPreviewRefresh', function () {
// ... hook code here ...
});
});
});
Note the extra postfix
parameter, which (generally) needs to be passed to the hook code. This parameter is needed to locate the correct preview pane, and is important for correctly and efficiently supporting multiple editor instances on one page (as might happen e.g. if the user wants to ask a question and immediately self-answer).
Specifically, the ID of the preview pane will be "wmd-preview" + postfix
; usually, you'd start your hook code by obtaining a jQuery object pointing to the preview pane with $("#wmd-preview" + postfix)
, and then work from there.