如何将CSS媒体查询的结果传递给JS?

时间:2018-08-08 17:54:35

标签: javascript html css leaflet

我有一个JS变量来控制页面的样式。我希望它根据屏幕分辨率进行更改。如何使其取决于CSS媒体查询的结果。

我知道我可以直接在JS中检查屏幕分辨率,但是我想将所有样式决定保留在CSS中,而不是将其散布在JS和CSS文件中。

为避免XY问题:我正在使用Leaflet,而JS变量决定列出可用图层的任何地图控制面板是否折叠。应该在小屏幕(移动设备)上折叠它,而不应该在大屏幕(适当的显示器)上折叠它。

相关代码在下面

html,
body {
  height: 100%;
  margin: 0;
}

#map {
  width: 600px;
  height: 400px;
}

@media (min-width: 1000px) {
  #map {
    width: 1000px;
    height: 900px;
  }
}
<!DOCTYPE html>
<html>
<!--based on https://leafletjs.com/examples/layers-control/ example -->

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.3/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
  <script src="https://unpkg.com/leaflet@1.3.3/dist/leaflet.js" integrity="sha512-tAGcCfR4Sc5ZP5ZoVz0quoZDYX5aCtEm/eu1KhSLj2c9eFrylXZknQYmxUssFaVJKvvc0dJQixhGjG2yXWiV9Q==" crossorigin=""></script>
  <link rel="stylesheet" href="example.css" />
</head>

<body>
  <div id='map'></div>
  <script>
    var positron = L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
        attribution: '#{openstreetmap_copyright_notice}, basemap: &copy; <a href=\"http://cartodb.com/attributions\">CartoDB</a>',
        subdomains: 'abcd',
        maxZoom: 19
      }),
      osmcarto = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      });

    var map = L.map('map', {
      center: [39.73, -104.99],
      zoom: 10,
      layers: [positron]
    });

    var baseLayers = {
      "positron": positron,
      "osm-carto": osmcarto
    };

    L.control.layers(baseLayers, {}, {
      collapsed: false
    }).addTo(map); //false/true should be specified by CSS
  </script>
</body>

</html>

3 个答案:

答案 0 :(得分:4)

您可以使用matchMedia进行一次检查,或者一次性(或更有用地)使用回调(有关回调选项here的更多信息):

window.matchMedia("(min-width: 1000px)").addListener(function(e) {
    if (e.matches) {
        // The window currently matches the query
    } else {
        // It doesn't
    }
});

为避免在JavaScript中重复查询文本,您可以搜索document.styleSheets并使用其media属性。可能不会很有趣,但是我无法想象有其他方法来避免重复媒体查询。

例如,这使用它发现的第一个screen and规则(jsFiddle):

// Find the rule
const rule = Array.from(document.styleSheets).reduce((found, sheet) => {
    return found || Array.from(sheet.cssRules || sheet.rules).find(rule => {
        return rule.conditionText && rule.conditionText.startsWith("screen and ");
    });
}, null);
if (rule) {
    const query = rule.conditionText.substring(11);
    console.log("rule found, setting listener for: " + query);
    window.matchMedia(query).addListener(function(e) {
        console.log("matches? ", e.matches);
    });
} else {
    console.log("No rule found");
}

自然地,您需要等待通过link元素加载的所有样式表都首先加载。

答案 1 :(得分:4)

漂亮的解决方案,但这并不意味着不对JS中的任何宽度属性进行硬编码。

在页面上添加一个虚拟的不可见元素。

<div id="dummy"></div>

#dummy::after {
  display: none;
  content: 'false';
}

@media (min-width: 1000px) {
  #dummy::after {
    content: 'true';
  }
}

然后在JS中读取content的值:

var isBigScreen = window.getComputedStyle(
    document.querySelector('#dummy'), ':after'
).getPropertyValue('content');

isBigScreen将是"true""false"。如果屏幕尺寸发生变化,您可能需要添加一些代码以重新检查。

if(isCollapsed === '"false"') {
    isCollapsed = false;
} else {
    isCollapsed = true;
}

可用于将其转换为标准布尔值。

答案 2 :(得分:2)

我唯一想到的方法是在CSS中设置某种样式,然后在Javascript中检查该样式。

例如类似于document.getElementById('map').style.width === '1000px'