WPBakery (Visual Composer) Bug in Change Handler

The WPBakery (Visual Composer) changeHandler function uses the “vc.accordion” data attribute without first checking for it’s existence.

Any plugin or theme using a jQuery show / hide event trigger (for example) will trip this bug, and the post editing page will fail to load properly.

I’ve posted the bug report to the Visual Composer channel on Slack, but the channel does not seem to be read by WPBakery employees, so this bug may continue to go unfixed.

The unminified WPBakery code looks like this. Note that data("vc.accordion") is being used here without first checking if the attribute exists or not.

changeHandler = function(e) {
    var caller;
    void 0 === (caller = $(e.target).data("vc.accordion")).getRelatedTab && (caller.getRelatedTab = function() {
        var findTargets;
        return findTargets = function() {
            return caller.getContainer().find("[data-vc-tabs]").filter(function() {
                var $this;
                return void 0 === ($this = $(this)).data("vc.accordion") && $this.vcAccordion(), $this.data("vc.accordion").getSelector() === caller.getSelector()
            })
        }, caller.isCacheUsed() ? (void 0 === caller.relatedTab && (caller.relatedTab = findTargets()), caller.relatedTab) : findTargets()
    }), Plugin.call(caller.getRelatedTab(), e.type)
}

There are many examples of jQuery code to trigger a show / hide event – here are two popular jQuery examples that will trip the WPBakery change handler bug. Other types of jQuery event handlers (click, hover, etc.) may also trip this bug, but I’ve only tested the jQuery show / hide events.

(function ($) {
    $.each(['show', 'hide'], function (i, ev) {
        var el = $.fn[ev];
        $.fn[ev] = function () {
            this.trigger(ev);
            return el.apply(this, arguments);
        };
    });
})(jQuery);
(function ($) {
    $.each(['show', 'hide', 'fadeOut', 'fadeIn'], function (i, ev) {
        var el = $.fn[ev];
        $.fn[ev] = function () {
            var result = el.apply(this, arguments);
            result.promise().done(function () {
                this.trigger(ev, [result]);
            });
            return result;
        };
    });
})(jQuery);