XF 2.1 Can't get preview element

CMTV

Well-known member
Hi!

In general, I need to analyze the DOM every time it is changed and maybe add some classes to inserted elements.
In particular, I am trying to get a preview element which appears after clicking on "Preview" button.

After researching the code for some time I figured out that I need to listen for xf:reinit event since it is called every time after XF.setupHtmlInsert function is successfully finished.

So here is my example code:
JavaScript:
(function ($, document)
{
    $(document).on('xf:reinit', function (a, el)
    {
        console.log($('.bbCodePreview-content'));
    });
})
(jQuery, document);

The problem is that I get nothing (length === 0). This happens only with "Preview" cases and works for all other inserted elements (tabs, menus, member profile panes) works fine...

It is even more strange that el variable is an added element. But why the hell it can't find it with $('.bbCodePreview-content')?!
 
Last edited:
This is sort of a bug with the previews, in that the activation code fires before the preview is in the DOM. This generally isn't a problem, though it's not really what's intended so I've adjusted the code to handle this more consistently.

Saying that, as you've said you need to analyze DOM changes, it sounds like you might actually want a mutation observer:

 
This is sort of a bug with the previews, in that the activation code fires before the preview is in the DOM.
Yep. It actually fires twice. The first one before the preview is in the DOM and this one fires activation (called when onReady ends). And the second one, but it actually does nothing since onCompleteRun is true so it just return; in the first condition in onComplete function (called directly within removeClassTransitioned function).

so I've adjusted the code to handle this more consistently
Do you mean this is "fixed" in closest release?

I am just writing a new version of my Math addon and I want to catch all HTML inserts to render math there (if some conditions met). Perhaps you are right and I should use MutationObserver but I am not sure it is okay to observe a whole document.

For XF 2.0.+ I used the following approach (basically, just overriding XF.setupHtmlInsert):
JavaScript:
(function ($, document)
{
    var oldSetupHtmlInsert = XF.setupHtmlInsert;
    XF.setupHtmlInsert = extendedSetupHtmlInsert;

    function extendedSetupHtmlInsert(container, onReady, retainScripts)
    {
        container.content += '<script>CMTV_Math.SResolver.resolve(); MathJax.Hub.Queue(["Typeset", MathJax.Hub]);</script>';
        oldSetupHtmlInsert.call(XF, container, onReady, retainScripts);
    }
})
(jQuery, document);

And it worked just fine. But this does not work in XF 2.1 plus it is not really good to alter container.content so I thought using xf:reinit would solve my problem.
 
Last edited:
Here is a workaround:
JavaScript:
(function ($, document)
{
    $(document).on('xf:reinit', function (e, $element)
    {
        if ($element === document)
        {
            return;
        }

        CMTV_Math.SResolver.resolve();
        MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
    });

    // Workaround
    $(function ()
    {
        var observer = new MutationObserver(function (mutationsList) {
            CMTV_Math.SResolver.resolve();
            MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
        });

        $('.js-previewContainer').each(function ()
        {
            observer.observe($(this).parent().get(0), { childList:true });
        });
    });
})
(jQuery, document);
 
Last edited:
The workaround above doesn't work with editing existing messages/threads.

Here is a full solution - a script called preview-observer.js:

JavaScript:
var CMTV_Code = window.CMTV_Code || {};

(function ($, document)
{
    "use strict";

    CMTV_Code.PreviewObserver = new MutationObserver(function (mutationsList)
    {
        var addedNodes = Array.from(mutationsList.shift().addedNodes);

        if (addedNodes.length)
        {
            var node = addedNodes.shift();

            // The preview element is in DOM right now
            // Looking for code blocks inside the message and triggering handler method
            // You can do whatever you want here
            $(node).find('.bbCodeBlock--code').trigger('render');
        }
    });
 
    $(document).on('xf:reinit', function (e, $element)
    {
        if ($element === document)
        {
            return;
        }

        registerObservers($element);
    });

    $(function ()
    {
        registerObservers($(document));
    });

    function registerObservers($element)
    {
        $element.find('.js-previewContainer').each(function ()
        {
            CMTV_Code.PreviewObserver.observe($(this).parent().get(0), { childList: true });
        });
    }
})
(jQuery, document);
 
Top Bottom