How can I pass results of CSS media query to JS? - javascript

I have a JS variable controlling styling of the page. I want it to change depending on screen resolution. How can I make it depend on results of CSS media query.
I know that I can check screen resolution directly in JS but I want to keep all styling decisions within CSS rather than spread it across JS and CSS files.
to avoid XY problem: I am using Leaflet and JS variable decides whatever map control panel listing available layers is collapsed or not. It should be collapsed on small screens (mobile) and not collapsed on large screens (proper monitors).
The relevant code is available below
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: © CartoDB',
subdomains: 'abcd',
maxZoom: 19
}),
osmcarto = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap 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>

Pretty cheezy solution, but it would mean not hardcoding any width properties in JS.
Add a dummy, invisible element to the page.
<div id="dummy"></div>
#dummy::after {
display: none;
content: 'false';
}
#media (min-width: 1000px) {
#dummy::after {
content: 'true';
}
}
Then read the value of the content in JS:
var isBigScreen = window.getComputedStyle(
document.querySelector('#dummy'), ':after'
).getPropertyValue('content');
isBigScreen will be either "true" or "false". You might want to add some code to recheck that if the screen size changes.
if(isCollapsed === '"false"') {
isCollapsed = false;
} else {
isCollapsed = true;
}
may be used to convert it to a standard boolean.

You can use matchMedia to do this, either as a one-off check or (more usefully) with a callback (more on the callback option here):
window.matchMedia("(min-width: 1000px)").addListener(function(e) {
if (e.matches) {
// The window currently matches the query
} else {
// It doesn't
}
});
To avoid repeating the query text in the JavaScript, you can search through document.styleSheets and using their media property. May not be a lot of fun, but I can't imagine any other way to avoid duplicating the media query.
For example, this uses the first screen and rule it finds (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");
}
Naturally, you'd need to wait for any stylesheets loaded via link elements to be loaded first.

The only way I could think of is by setting a certain style in your CSS and then checking for that style in Javascript.
e.g. something like document.getElementById('map').style.width === '1000px'

Related

How to create my custom control with event in Mapboxgl?

Using the mapboxgl, I added a my custom map control, which can do some special functions,and I refered the office example with basic function. But When I completed codes, I found the event click of button did not work. I have add the stopPropagation function. So how to add click event in the mapbox control?
mapboxgl.accessToken = 'pk.eyJ1IjoieGlhb2thbmciLCJhIjoiY2lqc2d2NXlyMGhkbHU0bTVtcGNiOWxseCJ9.J5qsX13KKNT1slMGS-MOLg';
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v9', // stylesheet location
center: [-74.50, 40], // starting position [lng, lat]
zoom: 9 // starting zoom
});
class ToggleControl {
onAdd(map){
this.map = map;
this.container = document.createElement('div');
this.container.className = 'my-custom-control';
const button = this._createButton('monitor_button')
this.container.appendChild(button);
return this.container;
}
onRemove(){
this.container.parentNode.removeChild(this.container);
this.map = undefined;
}
_createButton(className) {
const el = window.document.createElement('button')
el.className = className;
el.textContent = 'toggleControl';
el.addEventListener('click',(e)=>{
e.style.display = 'none'
console.log(e);
// e.preventDefault()
e.stopPropagation()
},false )
return el;
}
}
const toggleControl = new ToggleControl()
map.addControl(toggleControl,'top-left')
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Display a map</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
</script>
</body>
</html>
The problem is that the mapbox container positioning style removes mouse events handling (pointer-events: none;):
.mapboxgl-ctrl-top-left,
.mapboxgl-ctrl-top-right,
.mapboxgl-ctrl-bottom-left,
.mapboxgl-ctrl-bottom-right {
position: absolute;
pointer-events: none;
z-index: 2;
}
So you need to add the class mapboxgl-ctrl (pointer-events: auto;) to the container for catching events:
this.container.className = 'mapboxgl-ctrl my-custom-control';
Or set this CSS-property manually:
.my-custom-control {
pointer-events: auto;
}
[ https://jsfiddle.net/Lkes0ch8/ ]
I have the same problem, stdob-- is correct,
by add 'mapboxgl-ctrl' to className solved my problem.
without this, user can not drag the slider bar.
this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group opacityDIV';
Top right corner slider is what I created.
There are 2 way to add mapbox control,
standard way is from mapbox example,
alternative way is works the same
Here is my working code:

height=100% not rendering the google maps api

I am trying to use the Google Maps API. I have gotten a authorization key and pasted in the sample code they gave us, but I can't make the map render even when they tell me to make sure I put a height of 100% on both my map div and the body.
I have tried and it won't show up. If I put a 500px height on the map div, and give the body a 100% height it works... but I want to know why height of 100% will not will not render. I am copying exactly what the Google documentation (https://developers.google.com/maps/documentation/javascript/examples/map-simple) is giving me and what the video tells me to double check. HELP, i am insanely curious on why 100% height won't render. I want to use percentages, so that it can be compatible on mobile devices!
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>Ice Cream</title>
<!-- Google Docs-->
<!-- external CSS link -->
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/style.css">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
<div id='map_canvas'> HELLOooo <!--dimensions of map are set in CSS-->
</div>
<!-- external CSS link -->
<!-- javascript -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDuDCh7Odrr4RUUiAHMyDtfwC2NtySy-64"></script>
<script src="js/index.js"></script>
</body>
</html>
body, #map_canvas{
margin:0;
padding:0;
height: 100%;
}
$(function() {
console.log("hi");
function initialize() {
var mapOptions = {
center: { lat: -34.397, lng: 150.644},
zoom: 8
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
console.log('map');
});
The browser needs to be able to calculate a non-zero height. You need to set the height of the html element to 100% also:
$(function() {
console.log("hi");
function initialize() {
var mapOptions = {
center: { lat: -34.397, lng: 150.644},
zoom: 8
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
console.log('map');
});
html, body, #map_canvas{
margin:0;
padding:0;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id='map_canvas'> HELLOooo <!--dimensions of map are set in CSS-->
</div>
The reason why 100% doesn't work is because the map needs to send you images at a specific ratio (fixed width & height). Now I might be guessing here, but that's what I think happens.
Since I had a similar problem, where my map was hidden inside a tab, I wrote this function to calculate the width & height or a ratio. Then whatever size of the container, the map was in, it would adjust. It uses jQuery so I just thought I'd post it to get the idea.
/**
* Calculate the dimensions
*/
calculateMap: function () {
var self = this,
parent = this.map.parent(), // jQuery obj
w, h, id, el;
// if the container of your map is visible and wrapped it has a certain width and height...
// if it also has 100% height, it could be collapsed (not wrapped)
// so you ask the google maps api to load images at 500px width and 0height?
// if the parent has 0height, the ratio helps to set it properly
// with debug tools (F12) you can see the parent if it lights up
if (parent.is(':visible')) {
w = parent.outerWidth(false);
h = w * this.settings.options.mapRatio; // 4/3 | 16/9
this.map.css({ width: w, height: h });
} else {
// if your map is invisible after load...
id = this.onParent.prop('id'); // this id references my tab
el = this.onParent.prevAll().find('a[href=#' + id + ']'); // trigger of the tab
// add click event once
el.one(this.settings.events.onParent, function() {
setTimeout(function() {
self.activate(); // goes back to calculateMap()
}, 200);
}.bind(this));
}
},
If you trigger calculateMap on the resize event, you'll have a responsive approach.
Also a quick google showed me this approach ... notice the fixed width and height on the iframe.
In short:
fixed width and height in css gives you the cleanest solution. you might be able to use a clearfix, padding in % or min-height in px to gain dimension if that works out.
For dynamic/event loading I suggest JavaScript or jQuery
or with an iframe and a fixed width and height

How do I get the top left, bottom right latitude and longitude being shown in the map?

I want to call nokia.places.search.manager.findPlaces using a bounding box. I want the bounding box to correspond to the map being displayed on the page. I can't find the methods I would expect there to be at http://developer.here.com/maps_js.
The method you are after is Display.getViewBounds(). To quote from the API Reference:
This method retrieves the outer bounding box of the map view (the
smallest bounding box covering all visible points).
Here is an example using the boundingBox parameter in a places search (use your own app id and token of course). If you increase the zoomLevel of the map displayed, then the search is bounded to a smaller area. The important bit is right at the bottom.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>HERE Maps API Example: Search by bounding box</title>
<meta name="description" content="Search by category"/>
<meta name="keywords" content="search, services, places, category"/>
<!-- For scaling content for mobile devices, setting the viewport to the width of the device-->
<meta name=viewport content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<!-- By default we add ?with=all to load every package available, it's better to change this parameter to your use case. Options ?with=maps|positioning|places|placesdata|directions|datarendering|all -->
<script type="text/javascript" charset="UTF-8" src="http://api.maps.nokia.com/2.2.3/jsl.js?with=all"></script>
<!-- JavaScript for example container (NoteContainer & Logger) -->
<style type="text/css">
html {
overflow:hidden;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100%;
height: 100%;
position: absolute;
}
#mapContainer {
width: 80%;
height: 80%;
left: 0;
top: 0;
position: absolute;
}
#progress {
width: 80%;
height: 10%;
left: 0;
top: 80%;
position: absolute;
}
#buttons {
width: 80%;
height: 10%;
left: 0;
top: 90%;
position: absolute;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="progress"></div>
<script type="text/javascript" id="exampleJsSource">
nokia.Settings.set("appId", "YOUR APP ID");
nokia.Settings.set("authenticationToken", "YOUR TOKEN");
// Get the DOM node to which we will append the map
var mapContainer = document.getElementById("mapContainer");
// Create a map inside the map container DOM node
var map = new nokia.maps.map.Display(mapContainer, {
// Initial center and zoom level of the map
center: [52.51, 13.4],
zoomLevel: 15,
components: [
new nokia.maps.map.component.Behavior()
]
});
var searchManager = nokia.places.search.manager,
resultSet;
// Function for receiving search results from places search and process them
var processResults = function (data, requestStatus, requestId) {
var i, len, locations, marker;
if (requestStatus == "OK") {
// The function findPlaces() and reverseGeoCode() of return results in slightly different formats
locations = data.results ? data.results.items : [data.location];
// We check that at least one location has been found
if (locations.length > 0) {
// Remove results from previous search from the map
if (resultSet) map.objects.remove(resultSet);
// Convert all found locations into a set of markers
resultSet = new nokia.maps.map.Container();
for (i = 0, len = locations.length; i < len; i++) {
marker = new nokia.maps.map.StandardMarker(locations[i].position, { text: i+1 });
resultSet.objects.add(marker);
}
// Next we add the marker(s) to the map's object collection so they will be rendered onto the map
map.objects.add(resultSet);
// We zoom the map to a view that encapsulates all the markers into map's viewport
//map.zoomTo(resultSet.getBoundingBox(), true);
} else {
//alert("Your search produced no results!");
}
searching = false;
} else {
alert("The search request failed");
searching = false;
}
};
// Binding of DOM elements to several variables so we can install event handlers.
var progressUiElt = document.getElementById("progress");
var searching = false;
search = function(boundingBox){
// Make a place search request
var category = "eat-drink"
progressUiElt.innerHTML = "Looking for places in the '" + category + "' category...'";
searchManager.findPlacesByCategory({
category: category,
onComplete: processResults,
boundingBox: boundingBox,
limit: 50,
});
}
search(map.getViewBounds());
</script>
</body>
</html>

Is it possible to place a grid in Nokia maps?

Im using nokia maps javascript api, is posible place a grid in the map?
The easiest way to do this would be to use a transparent grid PNG as an overlay.
First create a 256x256 PNG file as shown below.
Then use that as your getTileUrl() function, so it is returned over all of the Map tiles on the map.
var getTileUrl = function (zoom, row, column) {
return "http://i.stack.imgur.com/M1ncK.png";
};
The result is something like this:
An example can be seen below, with your own PNG file, app id and token of course .
/* Set authentication token and appid
*
* please register on http://api.developer.nokia.com/
* and obtain your own developer's API key
*/
nokia.Settings.set("appId", "MY APP ID");
nokia.Settings.set("authenticationToken", "MY TOKEN");
// Get the DOM node to which we will append the map
var mapContainer = document.getElementById("mapContainer");
// Create a map inside the map container DOM node
var map = new nokia.maps.map.Display(mapContainer, {
// initial center and zoom level of the map
center: [52.515, 13.405],
zoomLevel: 14,
components: [
// ZoomBar provides a UI to zoom the map in & out
new nokia.maps.map.component.ZoomBar(),
// We add the behavior component to allow panning / zooming of the map
new nokia.maps.map.component.Behavior()
]
});
var getTileUrl = function (zoom, row, column) {
return "http://i.stack.imgur.com/M1ncK.png";
};
tileProviderOptions = {
getUrl: getTileUrl, // Obligatory function
max:20, // The highest zoom level for the overlay.
min:1, // The lowest zoom level for the overlay.
opacity: 0.5, // Overlay opacity.0 is fully transparent, 1 is fully opaque.
alpha:true // This value tells the renderer to read the alpha channel; required if opacity is used.
},
// Create an overlay by calling the constructor for ImgTileProvider
gridOverlay = new nokia.maps.map.provider.ImgTileProvider(tileProviderOptions);
// Add the overlay to the map
map.overlays.add(gridOverlay);
html {
overflow:hidden;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100%;
height: 100%;
position: absolute;
}
#mapContainer {
width: 100%;
height: 100%;
left: 0;
top: 0;
position: absolute;
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9; IE=EmulateIE10;"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Nokia Maps Example: Adding an overlay to the map</title>
<script type="text/javascript" charset="UTF-8" src="http://api.maps.nokia.com/2.2.4/jsl.js?with=all"></script>
<style type="text/css">
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="uiContainer"></div>
</body>
</html>

more than 20 results in nokia map 'nokia.places.search.manager.findPlaces' function?

using nokia maps "nokia.places.search.manager.findPlaces" i get upto 20 place search results.
how get more results?
my nokia function:
nokia.places.search.manager.findPlaces({
searchTerm: term,
searchCenter: searchCenterN,
didYouMean: 5
});
It looks like your best bet at the moment would be to use the limit parameter as shown. The default is 20 places, but it can be extended to 100.
searchManager.findPlacesByCategory({
category: category,
onComplete: processResults,
searchCenter: searchCenter,
limit: 50,
});
There is also a next URI buried in the result set, which accesses the RESTful Places API, but there doesn't appear to be any public function for processing that data yet in the 2.2.3 API.
Here is a fully working example based on the API Explorer example:
http://developer.here.net/apiexplorer/examples/api-for-js/places-search/places-search-by-category.html
You need to obtain your own free app id and token to get it to work.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!--
Example from Nokia Maps API Playground, for more information visit http://api.maps.nokia.com
-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Nokia Maps API Example: Search by category</title>
<meta name="description" content="Search by category"/>
<meta name="keywords" content="search, services, places, category"/>
<!-- For scaling content for mobile devices, setting the viewport to the width of the device-->
<meta name=viewport content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<!-- By default we add ?with=all to load every package available, it's better to change this parameter to your use case. Options ?with=maps|positioning|places|placesdata|directions|datarendering|all -->
<script type="text/javascript" charset="UTF-8" src="http://api.maps.nokia.com/2.2.3/jsl.js?with=all"></script>
<!-- JavaScript for example container (NoteContainer & Logger) -->
<style type="text/css">
html {
overflow:hidden;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100%;
height: 100%;
position: absolute;
}
#mapContainer {
width: 80%;
height: 80%;
left: 0;
top: 0;
position: absolute;
}
#progress {
width: 80%;
height: 10%;
left: 0;
top: 80%;
position: absolute;
}
#buttons {
width: 80%;
height: 10%;
left: 0;
top: 90%;
position: absolute;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="progress"></div>
<div id="buttons">
<a onClick="search1();return false;" href="#">Find 20 Places</a>
<a onClick="search2();return false;" href="#">Find 50 Places</a>
</div>
<script type="text/javascript" id="exampleJsSource">
/* Set authentication token and appid
* WARNING: this is a demo-only key
* please register on http://api.developer.nokia.com/
* and obtain your own developer's API key
*/
nokia.Settings.set("appId", "YOUR APP ID");
nokia.Settings.set("authenticationToken", "YOUR TOKEN");
// Get the DOM node to which we will append the map
var mapContainer = document.getElementById("mapContainer");
// Create a map inside the map container DOM node
var map = new nokia.maps.map.Display(mapContainer, {
// Initial center and zoom level of the map
center: [52.51, 13.4],
zoomLevel: 10,
components: [
new nokia.maps.map.component.Behavior()
]
});
var searchCenter = new nokia.maps.geo.Coordinate(52.51, 13.4),
searchManager = nokia.places.search.manager,
resultSet;
var resultDataJF;
// Function for receiving search results from places search and process them
var processResults = function (data, requestStatus, requestId) {
var i, len, locations, marker;
resultDataJF = data;
if (requestStatus == "OK") {
// The function findPlaces() and reverseGeoCode() of return results in slightly different formats
locations = data.results ? data.results.items : [data.location];
// We check that at least one location has been found
if (locations.length > 0) {
// Remove results from previous search from the map
if (resultSet) map.objects.remove(resultSet);
// Convert all found locations into a set of markers
resultSet = new nokia.maps.map.Container();
for (i = 0, len = locations.length; i < len; i++) {
marker = new nokia.maps.map.StandardMarker(locations[i].position, { text: i+1 });
resultSet.objects.add(marker);
}
// Next we add the marker(s) to the map's object collection so they will be rendered onto the map
map.objects.add(resultSet);
// We zoom the map to a view that encapsulates all the markers into map's viewport
map.zoomTo(resultSet.getBoundingBox(), false);
} else {
alert("Your search produced no results!");
}
} else {
alert("The search request failed");
}
};
// Binding of DOM elements to several variables so we can install event handlers.
var progressUiElt = document.getElementById("progress");
search1 = function(){
// Make a place search request
var category = "eat-drink";
progressUiElt.innerHTML = "Looking for places in the '" + category + "' category...'";
searchManager.findPlacesByCategory({
category: category,
onComplete: processResults,
searchCenter: searchCenter
});
}
search2 = function(){
// Make a place search request
var category = "eat-drink";
progressUiElt.innerHTML = "Looking for places in the '" + category + "' category...'";
searchManager.findPlacesByCategory({
category: category,
onComplete: processResults,
searchCenter: searchCenter,
limit: 50,
});
}
</script>
</body>
</html>

Categories