FranckFreiburger / vue-resize-sensor

detect container resizing
MIT License
76 stars 12 forks source link

Resize not firing on Chrome / IE 11 #1

Closed richardtallent closed 7 years ago

richardtallent commented 7 years ago

I have something like the following in my table component:

<table>
    <thead>...</thead>
    <tfoot><tr><td colspan="number of columns here">
        <resize-sensor @resize="onResize"></resize-sensor>
    </td></tr></tfoot>
    <tbody>...</tbody>
</table>

And in my methods:

onResize: function() {
    console.log("resizing");
}

The table resizes with the window, but onResize never fires. By adding other log statements directly to resize-sensor.vue, I can tell that the mounted fires, but scroll never does. I can also confirm via F12 tools that the resize sensor's template is being rendered properly in the DOM.

Placing the resize-sensor outside of the table inside the div that wraps it had no effect either. Not sure what to do to test this further.

FranckFreiburger commented 7 years ago

I cannot reproduce your issue with the following example:

<template>
    <table style="width:100%">
        <thead></thead>
        <tbody></tbody>
        <tfoot>
            <tr>
                <td colspan="10">
                    <resize-sensor @resize="onResize"></resize-sensor>
                </td>
            </tr>
        </tfoot>
    </table>
</template>

<script>
export default {
    components: {
        resizeSensor: require('vue-resize-sensor')
    },
    methods: {
        onResize: function(size) {

            console.log('resize', size);
        }
    }
}
</script>
richardtallent commented 7 years ago

I'm using display:flex on the table so I can scroll the tbody (which is why I need resize events--to sync the column header widths). Not sure if it's related to the issue. I haven't had time to construct a minimal test case yet.

richardtallent commented 7 years ago

I found two root causes in my case:

  1. If any ancestor of resize-sensor has display: none when the app is mounted, the resizer doesn't work, even after the display style is changed back to "block". (I set my div#app to display:none to avoid FOUC before Vue mounts it.)

  2. If any ancestor of resize-sensor is changed to display: none, it no longer detects resizing. This I suppose makes sense, but makes it more difficult to use in a context like a table, where I don't really want a footer.

For both of the above, using visibility:hidden or height:0 !important works around the issue, but I may not always have control over (1) (for example, if the sensor is in a hidden modal dialog component).

There's also some weird issue related to parents using display:flex and the if ( this.$el.offsetParent !== this.$el.parentNode ) line, but I can't seem to nail it down enough to give an example.

richardtallent commented 7 years ago

Yup, there's a problem with the line mentioned above when resize-sensor is placed in a flexbox table's tfoot's td -- the changing of the td's position to "relative" is the culprit. I'm a little over my head in the CSS, I can see the TR in that case doesn't extend across the row, which may be related.

FranckFreiburger commented 7 years ago

Hi Richard,

One important point is that the container of the resize-sensor must be positioned (absolute, relative, ... anything except static) on order to allow the resize-sensor to occupy the whole space of its container (see position: absolute; left: 0; top: 0; right: 0; bottom: 0 style).

For point (1), I can send an initial resize event when the component is mounted but think it is not possible to detect the case when an ancestor changed from display: none.

richardtallent commented 7 years ago

I was really hoping to not needlessly wrap my table component in another DIV, but I suppose that's the best way to get resize-sensor away from the weirdness of a flexbox table.

For (1), this is unfortunately an area where Vue's "props down, events up" approach fails -- not every important event is detectable at the child or dependent on a property. It's quite common to have components that are mounted but not displayed when the page is loaded (tabs, dialogs, etc.).

I can use the global bus to have elements that have hidden states (like tabs and modals) emit a message to any component with a resize sensor, and those components could then tell their resize sensors to reset, but I would feel "wrong" calling the resize sensor's mount() -- might be better for the setup logic to be extracted to another method so it isn't so tightly coupled to the lifecycle event.

FranckFreiburger commented 7 years ago

I think that flexbox is not an issue. The real issue is when the resize-sensor become visible after its creation. I think that my resize-sensor component should handle any borderline case by itself. The props down, events up model can really help you to keep big webapp consistent and modular.

I have added an event-based visibility detection (for modern browsers only (IE10+), fallback will poll). Please check v1.0.5

FranckFreiburger commented 7 years ago

ping!

richardtallent commented 7 years ago

I got distracted by other tasks and didn't get a chance to try it yet. I'll give it a shot!

richardtallent commented 7 years ago

Looks like it's working now, thanks!

FranckFreiburger commented 7 years ago

Good !