Skip to content
文章目录

window.matchMedia响应分辨率和操作系统主题模式变化

作用

  • 使用 window.matchMedia()在 JavaScript 中进行 CSS 媒体查询匹配。
  • 响应操作系统的主题变化

有些时候,除了 CSS 之外,你还需要在 JavaScript 中做一些匹配 CSS 媒体查询的事情。

window.matchMedia()返回的属性

js
// 示例
const mql = window.matchMedia('screen and (max-width: 765px)')

window.matchMedia()返回一个 MediaQueryList 对象,其中包含一些方法和属性,最常用的是 matches 属性。这是一个布尔属性,如果媒体查询与当前窗口的状态相匹配,则返回 true,如果不匹配则返回 false。

属性类型说明
matchesboolean如果当前窗口状态与 css 查询字符串中定义的条件匹配,则返回 true,否则返回 false
mediastring返回序列化的媒体列表. 如: 'screen and (max-width: 765px)'
js
var mql = window.matchMedia('screen and (min-width: 800px)')
if (mql.matches) {
  // 如果媒体查询匹配, 则说明当前屏幕最小宽度未800像素
  alert('窗口 >= 800px')
} else {
  // 做其他的事情
}

window.matchMedia()返回的方法

方法说明
addListener(function)增加响应函数,每当窗口发生变化时就会执行
removeListener(function)删除先前的响应函数

addEventListener()。通过一个函数来包装我们想要运行的代码,并将其添加到 addListener(),现在只要当前窗口状态的任何变化导致与定义的 CSS 媒体查询匹配,我们的函数就会被执行。

我们的代码现在不仅可以在运行时对 CSS 媒体查询匹配做出反应,而且可以在窗口状态发生变化时做出反应。

响应屏幕分辨率变化

js
function mediaqueryresponse(mql) {
  if (mql.matches) {
    console.log('屏幕宽度>=765px')
  } else {
    console.log('屏幕宽度<765px')
  }
}

var mql = window.matchMedia('(min-width: 765px)')
// 因为响应函数只在状态有变化时才会执行,因此这里需要手动执行一次
mediaqueryresponse(mql)
// 添加响应函数
mql.addEventListener(mediaqueryresponse)

封装 matchMedia

js
const reactiveQuery = {
  _subscribes: [], // 订阅的列表
  _query: null,
  _matchHandlers: [],
  _screens: {}, // 响应式变化更新的值
  _subUid: -1,
  /*
    订阅
  */
  subscribe(query, callback) {
    if (this._subscribes.length < 1) {
      this._query = query
      this.register()
    }

    const token = (++this._subUid).toString()
    this._subscribes.push({
      token,
      callback,
    })

    callback(this._screens, null)
    return token
  },
  /**
   * 注册响应式查询
   */
  register() {
    if (this._query) {
      Object.keys(this._query).forEach(key => {
        const mediaQueryString = this._query[key]
        if (!mediaQueryString) return

        const mediaQueryListListener = mql => {
          this.dispatch(
            {
              ...this._screens,
              [key]: mql.matches,
            },
            key
          )
        }

        const mql = window.matchMedia(mediaQueryString)
        mql.addEventListener(mediaQueryListListener)

        this._matchHandlers[mediaQueryString] = {
          mql,
          key,
          mediaQueryListListener,
        }

        mediaQueryListListener(mql)
      })
    }
  },
  /**
   * 将最新的 screens 通知给 callback 函数
   */
  dispatch(screens, screenDescribed) {
    this._screens = screens
    if (this._subscribes.length === 0) {
      return false
    }

    this._subscribes.forEach(subscribe => {
      subscribe.callback(screens, screenDescribed)
    })
  },

  /*
    取消对应的响应式订阅
  */
  unsubscribe(token) {
    this._subscribes = this._subscribes.filter(subscribe => subscribe.token !== token)
    /**
      在订阅为空时,清除所有事件响应器的绑定
    */
    if (this._subscribes) {
      this.unregister()
    }
  },

  /*
    清空所有的 mql.addListener 事件绑定
  */
  unregister() {
    Object.keys(this._query).forEach(key => {
      const mediaQueryString = this._query[key]
      if (!mediaQueryString) return

      const matchHandle = this._matchHandlers[mediaQueryString]
      if (matchHandle && matchHandle.mql && matchHandle.mediaQueryListListener) {
        matchHandle.mql.removeEventListener(matchHandle.mediaQueryListListener)
      }
    })
  },
}

使用

js
// 多条媒体查询语句
const query = {
  xs: '(max-width:576px)',
  sm: '(min-width:576px)',
  md: '(min-width:768px)',
  lg: '(min-width:992px)',
  xl: '(min-width:1200px)',
  xxl: '(min-width:1600px)',
}

reactiveQuery.subscribe(query, screen => {
  // 视口的变化符合任何一条查询语句时
  console.log('screen =>', screen)
})

响应操作系统主题变化

判断当前操作系统是否使用的是深色主题

js
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)')

if (prefersDarkScheme.matches) {
  // 用户系统主题设置为 dark
}

响应操作系统主题变化

js
function onOperateSystemChange(prefersDarkScheme) {
  if (prefersDarkScheme.matches) {
    // 用户操作系统主题设置为 dark
  } else {
    // 用户操作系统主题设置为 非dark
  }
}

const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)')
// 添加响应
prefersDarkScheme.addEventListener(onOperateSystemChange)
// 删除响应
prefersDarkScheme.removeListener(onOperateSystemChange)

参考资料

window.matchMedia 的应用与封装