我试图组织来自不同文件的JS代码。 我使用D3.js并创建了3个可视化对象,用户可以进行交互。例如,如果将鼠标悬停在第一个可视化的元素上,我想相应地更改第二个和第三个。
此过程使用此代码。
的index.html :
<body>
<span id='elem1' class='el'></span>
<span id='elem2' class='el'></span>
<span id='elem3' class='el'></span>
<script src='page1.js' rel='script'></script>
<script src='page2.js' rel='script'></script>
<script src='page3.js' rel='script'></script>
</body>
page1.js :
var PAGE1 = (function page1() {
// object to export
var moduleObj = {};
var elem1 = d3.select('#elem1')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem1rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#FF5733');
elem1.on('click', function() {
var elemClicked = 'elem1';
console.log('PAGE1 // Click on:', elemClicked);
if(moduleObj.clickElem1ToPage2Handler) {
moduleObj.clickElem1ToPage2Handler(elemClicked);
}
if(moduleObj.clickElem1ToPage3Handler) {
moduleObj.clickElem1ToPage3Handler(elemClicked);
}
});
return moduleObj;
}());
page2.js :
(function page2() {
var elem2 = d3.select('#elem2')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem2rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#FFC300');
elem2.on('click', function() {
var elemClicked = 'elem2';
console.log('PAGE2 // Click on:', elemClicked);
});
PAGE1.clickElem1ToPage2Handler = function(clickFrom) {
console.log('PAGE2 // Page2 received click from:', clickFrom);
};
})();
page3.js :
(function page3() {
var elem3 = d3.select('#elem3')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem3rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#DAF7A6');
elem3.on('click', function() {
var elemClicked = 'elem3';
console.log('PAGE3 // Click on:', elemClicked);
});
PAGE1.clickElem1ToPage3Handler = function(clickFrom) {
console.log('PAGE3 // Page3 received click from:', clickFrom);
};
})();
然而,现在,我希望能够做到反向,即通过将鼠标移到第二个可视化的元素上,修改第一个和第三个可视化。 同样,如果将鼠标悬停在第三个可视化的元素上,则会更改第一个和第二个。
在这两种情况下,我遇到了一个问题:使用之前使用的相同策略不再有效。 我修改了page1.js和page2.js。
page1.js :
var PAGE1 = (function page1() {
// object to export
var moduleObj = {};
var elem1 = d3.select('#elem1')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem1rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#FF5733');
elem1.on('click', function() {
var elemClicked = 'elem1';
console.log('PAGE1 // Click on:', elemClicked);
if(moduleObj.clickElem1ToPage2Handler) {
moduleObj.clickElem1ToPage2Handler(elemClicked);
}
if(moduleObj.clickElem1ToPage3Handler) {
moduleObj.clickElem1ToPage3Handler(elemClicked);
}
});
PAGE2.clickElem2ToPage1Handler = function(clickFrom) {
console.log('PAGE1 // Page1 received click from:', clickFrom);
};
return moduleObj;
}());
page2.js :
var PAGE2 = (function page2() {
// object to export
var moduleObj = {};
var elem2 = d3.select('#elem2')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem2rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#FFC300');
elem2.on('click', function() {
var elemClicked = 'elem2';
console.log('PAGE2 // Click on:', elemClicked);
if(moduleObj.clickElem2ToPage1Handler) {
moduleObj.clickElem2ToPage1Handler(elemClicked);
}
});
PAGE1.clickElem1ToPage2Handler = function(clickFrom) {
console.log('PAGE2 // Page2 received click from:', clickFrom);
};
return moduleObj;
})();
生成的错误是:
ReferenceError: PAGE2 is not defined (page1.js:27:2)
TypeError: PAGE1 is undefined (page2.js:24:2)
TypeError: PAGE1 is undefined (page3.js:17:2)
Here是整个(工作)代码。
有没有办法处理所有情况(可能无需更改整个结构)? 谢谢
答案 0 :(得分:5)
为了满足您不彻底改变整个结构的要求,我认为最简单的解决方法是采用事件驱动的通信体系结构。
您可以这样创建一个自定义事件:
cnt = contours[0] #--- since there is only one contour in the image
leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])
rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])
topmost = tuple(cnt[cnt[:,:,1].argmin()][0])
bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])
#--- drawing these points on the image ----
cv2.circle(im2, leftmost, 6, (0, 0, 255), -1)
cv2.circle(im2, rightmost, 6, (0, 0, 255), -1)
cv2.circle(im2, topmost, 6, (0, 255, 0), -1)
cv2.circle(im2, bottommost, 6, (0, 255, 0), -1)
cv2.imshow('final', im2)
然后您可以在其中一个元素与以下元素交互时分派此自定义事件:
d3Event = document.createEvent('Event');
d3Event.initEvent('d3TestClick', true, true);
还为每个组件添加事件处理程序,以供其他组件触发事件时使用:
elem1.on('click', function() {
var elemClicked = 'elem1';
console.log('PAGE1 // Click on:', elemClicked);
document.getElementById("elem1").dispatchEvent(d3Event);
});
P.S。我认为您的方法或我的建议也不会遵循最佳实践,因为它将组件之间紧密耦合并且阻碍了可伸缩性。
我相信解决此问题的最佳方法将是使组件不知道是否存在任何其他组件并由全局过滤器控制的方法。这样,您可以拥有无限的组件,并且它们都可以以自己的方式对全局过滤器所说的做出反应。
在Vanilla JS中实现这一目标感觉很艰巨。我可以建议为此使用诸如AngularJS之类的框架吗?
希望这会有所帮助:)
答案 1 :(得分:4)
根据您的要求,不做任何重大更改,这是最小代码更改,可避免该错误。您需要让不同页面一起交谈的部分就在这里,只是不够完整:
index.js 这是关键。为了更好地封装并避免范围问题,连接各个页面的所有代码都应位于执行其他脚本的文件中(在您的情况下为index.js)。
<body>
<span id='elem1' class='el'></span>
<span id='elem2' class='el'></span>
<span id='elem3' class='el'></span>
<script src='page1.js' rel='script'></script>
<script src='page2.js' rel='script'></script>
<script src='page3.js' rel='script'></script>
<script>
PAGE2.clickElem2ToPage1Handler = PAGE1.clickHandler;
</script>
</body>
page1.js 您的page1.js模块已经在返回一个对象,可以将其扩展为具有单击处理程序。 Page2 + 3应该做同样的事情-尽管我没有进行这些更改。
var PAGE1 = (function page1() {
// object to export
var moduleObj = {
clickHandler: function (clickedOn) {
console.log('PAGE1 // Page1 clicked on:', clickedOn);
}
};
var elem1 = d3.select('#elem1')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem1rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#FF5733');
elem1.on('click', function () {
var elemClicked = 'elem1';
console.log('PAGE1 // Click on:', elemClicked);
if (moduleObj.clickElem1ToPage2Handler) {
moduleObj.clickElem1ToPage2Handler(elemClicked);
}
if (moduleObj.clickElem1ToPage3Handler) {
moduleObj.clickElem1ToPage3Handler(elemClicked);
}
});
return moduleObj;
}());
我确实不得不更改了插件中的page2.js以匹配您在答案中发布的内容-它需要将moduleObj返回到PAGE2变量。我在具有这些更改的Plunker中制作了一个分叉(https://plnkr.co/edit/Uo6SJCZRQlyrKcVcjWEr-确保您选择了我所做的,我看不到直接链接到它的方法)。
如先前的回答所述,此架构根本无法很好地扩展到任何复杂性的应用程序。诸如Angular和React之类的框架可以为您解决这类问题。
如果您想知道收到的错误消息,ReferenceError: PAGE2 is not defined (page1.js:27:2)
是因为PAGE2直到page2.js中的函数执行后才在范围内。您可能习惯于函数在声明之前位于文件的作用域中,但是PAGE2并非如此,因为它是变量(即使变量指向函数,此处不是这种情况,变量在执行之前不会进入范围)。 PAGE1错误是因为到达return语句之前page1.js中的代码错误,因此不会发生index.js中的PAGE1变量赋值。
(最后的提示:处理程序中的console.log消息不能正确描述所有情况下的情况。我没有对此进行纠正。)
答案 2 :(得分:2)
您可以做的一件事是在立即调用的函数之外声明elem1
,elem2
和elem3
,然后创建一个单独的 eventHandlers.js 文件。在所有其他文件之后添加此文件,以便您可以访问变量。
例如:
index.html
<html lang='en'>
<head>
<meta charset='utf-8'>
<script src='https://d3js.org/d3.v5.js' charset='utf-8'></script>
<style>
.el {
margin-bottom: 0px;
padding-bottom: 0px;
}
</style>
</head>
<body>
<span id='elem1' class='el'></span>
<span id='elem2' class='el'></span>
<span id='elem3' class='el'></span>
<script src='page1.js' rel='script'></script>
<script src='page2.js' rel='script'></script>
<script src='page3.js' rel='script'></script>
<script src='eventHandlers.js' rel='script'></script>
</body>
</html>
page1.js
var elem1 = d3.select('#elem1')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem1rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#FF5733');
var PAGE1 = (function page1() {
// object to export
var moduleObj = {};
elem1.on('click', function() {
var elemClicked = 'elem1';
console.log('PAGE1 // Click on:', elemClicked);
moduleObj.clickHandler(elemClicked);
});
return moduleObj;
}());
page2.js
var elem2 = d3.select('#elem2')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem2rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#FFC300');
var PAGE2 = (function page2() {
// object to export
var moduleObj = {};
elem2.on('click', function() {
var elemClicked = 'elem2';
console.log('PAGE2 // Click on:', elemClicked);
moduleObj.clickHandler(elemClicked);
});
return moduleObj;
})();
page3.js
var elem3 = d3.select('#elem3')
.append('svg')
.append('g')
.append('rect')
.attr('id', 'elem3rect')
.attr('width', 50)
.attr('height', 50)
.attr('fill', '#DAF7A6');
var PAGE3 = (function page3() {
var moduleObj = {};
elem3.on('click', function() {
var elemClicked = 'elem3';
console.log('PAGE3 // Click on:', elemClicked);
moduleObj.clickHandler(elemClicked);
});
return moduleObj;
})();
eventHandlers.js
//can access elem1, elem2, and elem3 from here
PAGE1.clickHandler = function(clickFrom) {
console.log('Received click from', clickFrom);
};
PAGE2.clickHandler = function(clickFrom) {
console.log('Received click from', clickFrom);
};
PAGE3.clickHandler = function(clickFrom) {
console.log('Received click from', clickFrom);
};
使用此方法,您将在elem2
中编辑elem3
和PAGE1.clickHandler
,等等。
答案 3 :(得分:0)
我建议创建一个服务模块,该模块将在您拥有的所有其他模块之前调用,并具有要传递的信息的获取器和设置器。
它将用作中介,即数据存储。您所有其他文件都可以将其用作信息委托人。
答案 4 :(得分:0)
最简单的方法是使用全局对象,即使试图在操作的函数之外访问变量。一个简单的片段就可以。
window.var1= () => {
//var1 implemntation here
}
window.var2= () => {
//var2 implemntation here
}
window.var3= () => {
//var3 implemntation here
}
//If you want to access these even in some other scope you can do this
let test =() => {
//want var1 here
window.var1();
}