Javascript按钮onclick事件调用模块中函数内部的函数



我使用D3.js基于数据绘制和着色线条,并希望在单击按钮时更新它们的颜色我的问题是:如何从index.html中某个按钮的onclick事件调用在drawLines.js中的函数drawLines中声明的colorP1()colorP2()

我试过:

  1. 使用window.drawLines = drawLines技巧并让onclick事件引用window.drawLines.colorP2(),但我得到了Uncaught TypeError: colorP2 is not a function
  2. 使用window.colorP2 = colorP2,但我不知道在这种情况下导入将如何工作

有什么想法可以启发这位谦逊的初学者的思想吗?据我所知,colorP1()colorP2()必须留在drawLines()内部,因为它们需要drawLines()中的datalines变量——请放心,在这里证明我错了。

index.html

<html>
<head>
<style>
.line {
stroke-width: 4px;
fill: none;
}
</style>
</head>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script type="module">
import {drawLines} from './drawLines.js';
d3.json("test.geojson").then(drawLines);
</script>
<body>
<svg id='map'></svg>
<button onclick="colorP1()">colorP1</button>
<button onclick="colorP2()">colorP2</button>
</body>
</html>

drawLines.js

function colorInterpolate(data, property) {
let max_d = d3.max(data.features.map(d => d.properties[property]));
let range = [max_d, 1];
return d3.scaleSequential().domain(range).interpolator(d3.interpolateViridis); 
}
export function drawLines(data) {
let width = 900,
height = 500,
initialScale = 1 << 23,
initialCenter = [-74.200698022608137, 40.034504451003734]
let svg = d3.select('#map')
.attr('height', height)
.attr('width', width)
let projection = d3.geoMercator()
.scale(initialScale)
.center(initialCenter)
.translate([width / 2, height / 2])
let path = d3.geoPath(projection)
let myColor = colorInterpolate(data, 'p1');
let lines = svg.append('g')
lines.selectAll('path')
.data(data.features)
.join('path')
.attr('class', 'line')
.attr('d', path)
.attr("stroke", function(d) {
return myColor(d.properties.p1);
})
function colorP2() {
let myColor = colorInterpolate(data, 'p2');
lines.selectAll('path')
.attr("stroke", d => myColor(d.properties.p2))
}
function colorP1() {
let myColor = colorInterpolate(data, 'p1');
lines.selectAll('path')
.attr("stroke", d => myColor(d.properties.p1))
}
}

test.geojson

{
"type": "FeatureCollection",
"name": "lines",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "id": 3, "p1": 1, "p2": 3}, "geometry": { "type": "LineString", "coordinates": [ [ -74.201304101157845, 40.033790926216739 ], [ -74.201226425025339, 40.033761910802717 ], [ -74.201164135201353, 40.033738641825124 ] ] } },
{ "type": "Feature", "properties": { "id": 4, "p1": 2, "p2": 2}, "geometry": { "type": "LineString", "coordinates": [ [ -74.200521185229846, 40.034804885753857 ], [ -74.200535458528648, 40.034780636493231 ], [ -74.200698022608137, 40.034504451003734 ], [ -74.200932444446437, 40.034106179618831 ], [ -74.201017665586349, 40.033961391736824 ] ] } }
]
}

您的假设是错误的:

据我所知,colorP1((和colorP2((必须留在drawLines((中,因为它们需要来自drawLine((的数据和行变量

D3将数据绑定到使用.data(data).join().data(data).enter()输入的元素。基准附着到节点。当使用.attr("something",function(d) {时,d指的是绑定数据,而不是原始数据数组。因此,您不需要原始数据数组,它是DOM元素的一部分。

此外,您不需要lines,因为您可以重制该选择:d3.selectAll("paths")d3.selectAll(".line")

因此,您可以将p1/p2函数移动到drawLines函数之外。

正如我想为下面的片段简化的那样,我有一个函数,它传递数据来画一些圆。然后,我将事件侦听器分配给按钮(我也可以直接在按钮上使用onclick=""属性(和D3来调用对圆圈重新着色的函数:

function color1() {
d3.selectAll("circle")
.attr("fill",d=>d.color1);
}

函数访问绑定的数据和给定的属性(d=>d.color1(,通过使用d3.selectAll(),我们可以选择点击时存在的所有圆:

function draw(data) {
var svg = d3.select("body")
.append("svg")
.attr("width", 300)
.attr("height", 200);

svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx",d=>d.x)
.attr("cy",d=>d.y)
.attr("fill",d=>d.color2)
.attr("r", 20);


}
draw([{x: 100,y:50, color1: "steelblue",color2:"crimson"},{x:200,y:50,color1:"steelblue",color2:"crimson"}])
d3.selectAll("button")
.data([0,1])
.on("click", function(event,d) {
if (d) color2();
else color1();
})

function color1() {
d3.selectAll("circle")
.attr("fill",d=>d.color1);
}
function color2() {
d3.selectAll("circle")
.attr("fill",d=>d.color2);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<div>
<button> Blue </button>
<button> Red </button>
</div>

如果您需要数据数组本身,您可以使用d3.selectAll("elements").data()提取

当然,我们也可以在drawLines函数中添加按钮,这可能会产生更干净的结果,特别是如果按钮依赖于任何形式的数据。这样,如果你想更改按钮或功能,一切都在一个地方,例如:

var geojson = { "type": "FeatureCollection","name": "lines","crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },"features": [{ "type": "Feature", "properties": { "id": 3, "p1": 1, "p2": 3}, "geometry": { "type": "LineString", "coordinates": [ [ -74.201304101157845, 40.033790926216739 ], [ -74.201226425025339, 40.033761910802717 ], [ -74.201164135201353, 40.033738641825124 ] ] } },{ "type": "Feature", "properties": { "id": 4, "p1": 2, "p2": 2}, "geometry": { "type": "LineString", "coordinates": [ [ -74.200521185229846, 40.034804885753857 ], [ -74.200535458528648, 40.034780636493231 ], [ -74.200698022608137, 40.034504451003734 ], [ -74.200932444446437, 40.034106179618831 ], [ -74.201017665586349, 40.033961391736824 ] ] } }]};
function drawLines(data) {
let width = 500,
height = 400,
initialScale = 1 << 23,
initialCenter = [-74.200698022608137, 40.034504451003734]
let svg = d3.select('#map')
.attr('height', height)
.attr('width', width)
let projection = d3.geoMercator()
.fitSize([width,height],data)
let path = d3.geoPath(projection)
let myColor = colorInterpolate(data, 'p1');
let lines = svg.append('g')
lines.selectAll('path')
.data(data.features)
.join('path')
.attr('class', 'line')
.attr('d', path)

colorBy("p1");
function colorBy(property) {
let myColor = colorInterpolate(property);
lines.selectAll('path')
.attr("stroke", d => myColor(d.properties[property]))
}

function colorInterpolate(property) {
let max_d = d3.max(data.features.map(d => d.properties[property]));
let range = [max_d, 1];
return d3.scaleSequential().domain(range).interpolator(d3.interpolateViridis); 
}

d3.selectAll(".property")
.data(["p1","p2"])
.enter()
.append("button")
.attr("class","property")
.text(d=>d)
.on("click", function(_,d) {
colorBy(d);
})
.lower();
}
drawLines(geojson);
.line {
stroke-width: 4px;
fill: none;
}
<script src="https://d3js.org/d3.v6.min.js"></script>
<svg id='map'></svg>

您可以这样调用来调用内部函数:

<button onclick="(new drawLines().colorP1())">colorP1</button>
<button onclick="(new drawLines().colorP2())">colorP2</button>

和继续工作的工作示例

var json1 ='{ "type" : "FeatureCollection", "name":"lines",  "crs": { "type": "name", "properties":{ "name":"urn:ogc:def:crs:OGC:1.3:CRS84" }}, "features" : [{  "type" : "Feature", "properties" : { "id" : 3, "p1" : 1, "p2": 3}, "geometry" : {"type" : "LineString","coordinates":[[ -74.201304101157845, 40.033790926216739],[-74.201226425025339,40.033761910802717 ],[-74.201164135201353,40.033738641825124]]}},{"type": "Feature","properties":{ "id" : 4, "p1" : 2, "p2" :2 },"geometry" : { "type": "LineString", "coordinates" : [[ -74.200521185229846, 40.034804885753857 ],[ -74.200535458528648, 40.034780636493231 ],[ -74.200698022608137, 40.034504451003734 ],[ -74.200932444446437, 40.034106179618831 ],[ -74.201017665586349, 40.033961391736824 ]]}}]}';
var width = 900,
height = 500,
initialScale = 1 << 23,
initialCenter = [-74.198698022608137, 40.034504451003734]

var svg = d3.select('#map')
.attr('height', height)
.attr('width', width);
var lines = svg.append('g');
var projection = d3.geoMercator()
.scale(initialScale)
.center(initialCenter)
.translate([width / 2, height / 2])
var path = d3.geoPath(projection)
function colorInterpolate(data, property) {
let max_d = d3
.max(data.features.map(d => d.properties[property]));
let range = [max_d, 1];    
return d3.scaleSequential()
.domain(range)
.interpolator(d3.interpolateViridis); 
}
function drawLines(data) {
let myColor = colorInterpolate(data, 'p1');

lines.selectAll('path')
.data(data.features)
.join('path')
.attr('class', 'line')
.attr('d', path)
.attr("stroke", function(d) {
return myColor(d.properties.p1);
});
}
function colorP2(data){
let myColor = colorInterpolate(data, 'p2');
lines.selectAll('path')
.attr("stroke", d=>myColor(d.properties.p2));
}
function colorP1(data){
let myColor = colorInterpolate(data, 'p1');
lines.selectAll('path')
.attr("stroke", d=>myColor(d.properties.p1));
}
<html>
<head>
<style>
.line {
stroke-width: 4px;
fill: none;
}
</style>
</head>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script type="module">
//import {drawLines} from './drawLines.js';
//d3.json("test.geojson").then(drawLines);
drawLines(JSON.parse(json1));
</script>
<body>
<svg id='map'></svg>
<button onclick="colorP1(JSON.parse(json1))">colorP1</button>
<button onclick="colorP2(JSON.parse(json1))">colorP2</button>
</body>
</html>

最新更新