skip to Main Content

im trying to use v-if to render 2 buttons in different containers on page, depending on screen size
is there way how to change parent of items in @media or way how to use screen size in v-if condition?

i tried

<div v-if="window.innerWidth < 860">
<!-- Some code -->
</div>

but it throws me "TypeError: Cannot read properties of undefined (reading ‘innerWidth’)"

(when i use just media query, there appears bug with radio btns, when they needs 2 clicks to get :checked styles)
when i tried @media, in fact there was 4 buttons, that binded to 2 variables

<!-- button code -->
<label>
  <input type="radio" name="month-season" id="month" value="month" v-model="monthSeason">
  <null-button>
     <img src="../assets/green-smile.png" alt="#" id="month-icon">
     text
  </null-button>
</label>


<!-- null-button -->
<div class="parent">
  <div class="container">
    <slot></slot>
  </div>
<div>

<!-- styles for button -->
<style>
    .month-season {
        text-align: right;

        label {
            .container {
                color: rgba(45, 206, 137, 1);
                background: white;
                img {
                    margin: 0 4px 0 0;
                }
            }
            input:checked + .parent .container {
                background: rgba(45, 206, 137, 1);
                color: white;
                img {
                    filter: brightness(1000%);
                }
            }
            input {
                appearance: none;
            }
        }
    }
</style>

Error message when I use window.innerWidth
when using window.innerWidth

2

Answers


  1. Vue3 SFC Playground

    All JS expression that you use in a template are executed in a so called template context. It contains all stuff declared in a component and does NOT contains JS globals like window and its props like alert. So you should use window outside a template or to add to app.config.globalProperties.

    But adding window to the context doesn’t solve you problem because JS expressions using it aren’t reactive and your code would work only on initial component rendering.

    So you should go reactive here. A common Vue3 approach would be using composables here.

    We could use 2 different approaches here. Either using matchMedia or window.innerWidth as you tried.

    With matchMedia you can go with any CSS media query you like.

    So in the both cases you listen for appropriate events and update ref that is used further in a template.

    If you listen on window don’t forget to remove the listener when the component is unmounted otherwise you will have problems of ghost event handlers.

    So your matchMedia composable:

    import {ref} from 'vue';
    
    export function useMatchMedia(query){
    
        const match = window.matchMedia(query);
    
        const isMatching = ref(match.matches);
    
        match.addEventListener('change', e => isMatching.value = e.matches);
    
        return isMatching;
    }
    

    Your window.innerWidth composable:

    import {ref, onUnmounted} from 'vue';
    
    export function useInnerWidth(query){
    
        const match = window.matchMedia(query);
    
        const width = ref(window.innerWidth);
    
        const syncWidth = () => width.value = window.innerWidth;
    
        window.addEventListener('resize', syncWidth);
    
        onUnmounted(() => window.removeEventListener('resize', syncWidth));
    
        return width;
    }
    

    And the usage:

    <script setup>
    
    import { useMatchMedia } from './useMatchMedia';
    const smallScreen = useMatchMedia('(max-width: 859px)');
    
    import { useInnerWidth } from './useInnerWidth';
    const innerWidth = useInnerWidth();
    
    </script>
    
    <template>
      <h1 v-if="smallScreen">I'm a smaller screen</h1>
      <h1 v-else>I'm a bigger screen</h1>
      <p v-if="innerWidth < 860">Smaller screen width: {{innerWidth}}</p>
      <p v-else>Bigger screen width: {{innerWidth}}</p>
    </template>
    
    Login or Signup to reply.
  2. Maybe try using useWindowSize or useElementSize.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search