I would like to get the SVG elements for some given coordinates.
I tried to use document.elementsFromPoint(x,y)
. However, it only returns the main svg
element itself, not the sub elements (circles, paths etc) inside svg.
=>How can I find the SVG elements for given coordinates?
Example html file where I want to move a red circle on a green path by pressing arrow keys. Movement should only be allowed if the circle stays on on the green path.
Screenshot:
Demo:
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
function onClick(){
alert('You have clicked the circle.')
}
function onKeyPress(event){
switch(event.keyCode){
case 37: moveLeft();
break;
case 38: moveUp();
break;
case 39: moveRight();
break;
case 40: moveDown();
break;
default:
}
}
function moveDown(){
console.log('down');
var path = d3.select('#path');
var robot = d3.select('#robot');
var cx = Number(robot.attr('cx'));
var cy = Number(robot.attr('cy'));
var newcy = cy 10;
var elements = document.elementsFromPoint(cx, newcy)
if(path in elements){
robot.attr('cy', cy 10);
}
}
function moveUp(){
console.log('up');
var robot = d3.select('#robot');
var cy = Number(robot.attr('cy'));
robot.attr('cy', cy-10);
}
function moveLeft(){
console.log('left')
var robot = d3.select('#robot');
var cx = Number(robot.attr('cx'));
robot.attr('cx', cx-10);
}
function moveRight(){
console.log('right');
var robot = d3.select('#robot');
var cx = Number(robot.attr('cx'));
robot.attr('cx', cx 10);
}
function onLoad(){
console.log('onload')
this.addEventListener('keydown', event => onKeyPress(event));
}
</script>
<svg
width='500px'
height='500px'
focusable
onload="onLoad()"
>
<text x='0' y='20' fill='blue'>Hello world from within svg! Press arrow keys to move the circle:</text>
<path id="path" d="M100 100 L 100 200 L 200 200" stroke='green' fill="transparent"/>
<circle id="robot" cx="100" cy="100" r="5" fill='red' onclick="onClick()" />
</svg>
</body>
</html>
CodePudding user response:
Because Document.elementsFromPoint() is relative to the viewport you can use Element.getBoundingClientRect() that also is relative to the viewport. So, the robot position (the center) is the result of the x/y position plus half the width/height. And after finding the array of elements you can test if indexOf()
is more than -1.
svg {
margin: 15px;
}
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
function onClick() {
alert('You have clicked the circle.')
}
function onKeyPress(event) {
switch (event.keyCode) {
case 37:
moveLeft();
break;
case 38:
moveUp();
break;
case 39:
moveRight();
break;
case 40:
moveDown();
break;
default:
}
}
function moveDown() {
console.log('down');
//var path = d3.select('#path');
var path = document.getElementById('path');
var robot = d3.select('#robot');
var cx = Number(robot.attr('cx'));
var cy = Number(robot.attr('cy'));
let robotrect = document.querySelector('#robot').getBoundingClientRect();
let posx = robotrect.x robotrect.width / 2;
let posy = robotrect.y robotrect.height / 2;
var newcy = cy 10;
var elements = document.elementsFromPoint(posx, posy 10);
if (elements.indexOf(path) > -1) {
robot.attr('cy', newcy);
}
}
function moveUp() {
console.log('up');
var robot = d3.select('#robot');
var cy = Number(robot.attr('cy'));
robot.attr('cy', cy - 10);
}
function moveLeft() {
console.log('left')
var robot = d3.select('#robot');
var cx = Number(robot.attr('cx'));
robot.attr('cx', cx - 10);
}
function moveRight() {
console.log('right');
var robot = d3.select('#robot');
var cx = Number(robot.attr('cx'));
robot.attr('cx', cx 10);
}
function onLoad() {
console.log('onload')
this.addEventListener('keydown', event => onKeyPress(event));
}
</script>
<svg width='500px' height='500px' focusable onload="onLoad()">
<text x='0' y='20' fill='blue'>Hello world from within svg! Press arrow keys to move the circle:</text>
<path id="path" d="M100 100 L 100 200 L 200 200" stroke='green' fill="transparent"/>
<circle id="robot" cx="100" cy="100" r="5" fill='red' onclick="onClick()" />
</svg>
</body>
</html>