yiisoft / yii2-bootstrap

Yii 2 Bootstrap 3 Extension
http://www.yiiframework.com
BSD 3-Clause "New" or "Revised" License
185 stars 193 forks source link

Collapse widget calls bad inline-script in HTML #250

Closed voodooism closed 5 years ago

voodooism commented 5 years ago

What steps will reproduce the problem?

I have nested collapse interface, like this:

index.php

<?= Collapse::widget([
    'items' => [
        [
            'label' => 'Collapse1',
            'content' => $this->render('1'),
        ],
        [
            'label' => 'Collapse2',
            'content' => $this->render('2'),
        ],
        [
            'label' => 'Collapse3',
            'content' => $this->render('3'),
        ],
    ]
]); ?>

1.php

<?= Collapse::widget([
    'items' => [
        [
            'label' => 'Collapse1',
            'content' => $this->render('1_1'),
        ],
        [
            'label' => 'Collapse2',
            'content' => $this->render('1_2'),
        ],
        [
            'label' => 'Collapse3',
            'content' => $this->render('1_3'),
        ],
    ]
]); ?>

and so on.

What is the expected result?

I want to open the collapse by Id and scroll page to it when a page is loading. It's my js code:

$(function() {
    var searchParams = new URLSearchParams(location.search);
    var anchor = searchParams.get('anchor');
    if (!anchor) {
        throw new TypeError('There is no "anchor" parameter in the URL');
    }
    openCollapse(anchor);

$('#' + anchor).on('shown.bs.collapse', function () {
    this[0].scrollIntoView({
        behavior: 'smooth',
        block: 'start'
    });
});

});

function openCollapse (elementId) {
    var targetCollapse = $('#' + elementId);
    if (!targetCollapse.hasClass('collapse')) {
        throw new TypeError('error');
    }
    var parents = targetCollapse.parents('.panel-collapse').toArray().reverse();
    parents.push(targetCollapse[0]);

    $.each(parents, function (key, object) {
        $(object).collapse('show');
    });
}

But scroll doesn't work this, because event shown.bs.collapse works not correctly.

What do you get instead?

I register my js-code after all dependencies, but Collapse widget makes an inline script in HTML(see screenshot) That's why it happens. After calling this function jQuery('#w0').collapse(); the shown.bs.collapse event occur(although visually nothing happens). You can verify this with the following js-code.

$('.collapse').on('shown.bs.collapse', function () {
    console.log(this);
});

Thus, when the page is loaded, shown.bs.collapse event has already been triggered several times. I think this is incorrect.

Finally, I can make my js-code work only by placing it at the end of the inline-script in HTML using the $this->registerJs(<<<CODE 'mycode' CODE, View:POS_READY) method.

Since this behavior occurs for this reason, I think this is a framework problem. Or not?

voodooism commented 5 years ago

Maybe it will be better?

        if ($this->clientOptions !== false && !empty($this->clientOptions()) {
            $js = "jQuery('#$id').$name(" . Json::htmlEncode($this->clientOptions); . ");";
            $view->registerJs($js);
        }

then https://github.com/yiisoft/yii2-bootstrap/blob/master/src/BootstrapWidgetTrait.php#L77

samdark commented 5 years ago

According to bootstrap docs the call is required to enable "collapse" for an element specified: https://getbootstrap.com/docs/3.4/javascript/#via-javascript-3. If it triggers the event then it's designed like so by bootstrap team and we can't really affect it.