我正在 RMarkdown 中渲染一个 mapboxgl 地图(HTML 输出,在 R 中使用 r2d3 js 库作为 js 部分)。我正在尝试通过传入地图填充属性的动态变量(基于用户在下拉列表中选择的内容)来更新 mapboxgl 地图中显示的图层。
//set default property for map so it knows which field to use for the map fill color
var dropdown_val = 'ODpc'
//function that initially draws the map
function loadMap(dropdown_val) {
map2.on('load', function () {
map2.addSource('bb', { type: 'geojson', data: data, generateId: true});
map2.addLayer({
'id': 'berlin',
'type': 'fill',
'source': 'bb',
'paint': {
'fill-color': {
'property': dropdown_val,
'stops': [[break1, '#feebe2'], [break2, '#fbb4b9'], [break3, '#f768a1'], [break4, '#c51b8a'], [break5, '#7a0177']]
},
'fill-opacity': .65
}
});
map2.addLayer({
'id': 'berlin-stroke',
'type': 'line',
'source': 'bb',
'paint': {
'line-color': '#000',
'line-width': [
'case',
['boolean', ['feature-state', 'hover'], false],
2,
.5
]
}
});
});
}
loadMap(dropdown_val)
//call the function that adds the year dropdown, other map elements like legend, etc.
document.body.onload = addElement;
function addElement() {
//Create array of options to be added
var array = ["2014-2022", "2022","2021","2020","2019", "2018", "2017", "2016", "2015", "2014"];
var val_array = ["ODpc", "ODpc2022","ODpc2021","ODpc2020","ODpc2019", "ODpc2018", "ODpc2017", "ODpc2016", "ODpc2015", "ODpc2014"];
//Create and append select list
var selectList = document.createElement("select");
selectList.id = "mySelect";
selectList.className = "mySelect";
//Create and append the options
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
option.value = val_array[i];
option.text = array[i];
selectList.appendChild(option);
}
//make legend etc.. etc...
});
// create a function that gets the current value of the dropdown
document.getElementById('mySelect').onchange = function(){
dropdown_val = document.getElementById('mySelect').value;
return dropdown_val
}
这按预期工作 - 初始地图渲染良好,我可以按预期返回当前下拉值。
我的问题:我想根据下拉列表的当前值动态更新地图。当我添加
document.getElementById('mySelect').onchange
事件侦听器(根据下拉值更新地图)时,我遇到了问题:
//set default property for map so it knows which field to use for the map fill color
var dropdown_val = 'ODpc'
//function that initially draws the map
function loadMap(dropdown_val) {
map2.on('load', function () {
map2.addSource('bb', { type: 'geojson', data: data, generateId: true});
map2.addLayer({
'id': 'berlin',
'type': 'fill',
'source': 'bb',
'paint': {
'fill-color': {
'property': dropdown_val,
'stops': [[break1, '#feebe2'], [break2, '#fbb4b9'], [break3, '#f768a1'], [break4, '#c51b8a'], [break5, '#7a0177']]
},
'fill-opacity': .65
}
});
map2.addLayer({
'id': 'berlin-stroke',
'type': 'line',
'source': 'bb',
'paint': {
'line-color': '#000',
'line-width': [
'case',
['boolean', ['feature-state', 'hover'], false],
2,
.5
]
}
});
});
}
loadMap(dropdown_val)
//call the function that adds the year dropdown, other map elements like legend, etc.
document.body.onload = addElement;
function addElement() {
//Create array of options to be added
var array = ["2014-2022", "2022","2021","2020","2019", "2018", "2017", "2016", "2015", "2014"];
var val_array = ["ODpc", "ODpc2022","ODpc2021","ODpc2020","ODpc2019", "ODpc2018", "ODpc2017", "ODpc2016", "ODpc2015", "ODpc2014"];
//Create and append select list
var selectList = document.createElement("select");
selectList.id = "mySelect";
selectList.className = "mySelect";
//Create and append the options
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
option.value = val_array[i];
option.text = array[i];
selectList.appendChild(option);
}
//make legend etc.. etc...
});
//update the map based on the dropdown value
$(document).ready(function() {
document.getElementById('mySelect').onchange = function(){
//remove any existing layers so that they can be re-drawn according to whatever the user has selected in dropdown
if (map2.getLayer('berlin')) {
map2.removeLayer('berlin');
}
if (map2.getLayer('berlin-stroke')) {
map2.removeLayer('berlin-stroke');
}
if (map2.getLayer(dropdown_val)) {
map2.removeLayer(dropdown_val);
}
if (map2.getLayer('stroke')) {
map2.removeLayer('stroke');
}
dropdown_val = document.getElementById('mySelect').value;
//call function to load in current dropdown value and re-draw map fill to be based on that property
map2.addLayer({
'id': dropdown_val,
'type': 'fill',
'source': 'bb',
'paint': {
'fill-color': {
'property': 'ODpc'+dropdown_val,
'stops': [[break1, '#feebe2'], [break2, '#fbb4b9'], [break3, '#f768a1'], [break4, '#c51b8a'], [break5, '#7a0177']]
},
'fill-opacity': .65
}
});
map2.addLayer({
'id': 'stroke',
'type': 'line',
'source': 'bb',
'paint': {
'line-color': '#000',
'line-width': [
'case',
['boolean', ['feature-state', 'hover'], false],
2,
.5
]
}
})
}
})
在
document.getElementById('mySelect').onchange = function()
行我收到错误:
Uncaught TypeError: Cannot set properties of null (setting 'onchange')
地图实际上会使用 onchange 函数进行更新,它只是有点小故障,有时无法工作,具体取决于页面加载的方式。
似乎错误可能是由于在 DOM 中实际渲染下拉列表之前调用了
onchange
函数?但是,如果我将 onchange
函数移到 addElement()
函数(在 document.body.onload
上调用)中,它根本不起作用(我没有收到错误消息,但地图图层没有被删除并且重新绘制)。
提前致谢。
现在,这是一个疯狂的猜测,但如果您创建下拉列表并在 map2 加载函数中订阅它,则不应该存在竞争条件。
//set default property for map so it knows which field to use for the map fill color
var dropdown_val = 'ODpc'
//function that initially draws the map
function loadMap(dropdown_val) {
map2.on('load', function () {
// Add source and layers, etc...
// Create and inside the load function instead.
addElement();
});
}
loadMap(dropdown_val)
function addElement() {
//Create array of options to be added
var array = ["2014-2022", "2022","2021","2020","2019", "2018", "2017", "2016", "2015", "2014"];
var val_array = ["ODpc", "ODpc2022","ODpc2021","ODpc2020","ODpc2019", "ODpc2018", "ODpc2017", "ODpc2016", "ODpc2015", "ODpc2014"];
//Create and append select list
var selectList = document.createElement("select");
selectList.id = "mySelect";
selectList.className = "mySelect";
//Create and append the options
for (var i = 0; i < array.length; i++) {
var option = document.createElement("option");
option.value = val_array[i];
option.text = array[i];
selectList.appendChild(option);
}
//make legend etc.. etc...
// Connect the event listener
selectList.addEventListener('change', onChange);
// Add the element where you want it, for example body.
body.appendChild(selectList)
};
function onChange(event) {
const dropdown_val = event.target.value;
// Update the layers depending on the value
}