Reputation: 6653
I've got an svg element with an foreignObject
inside of it with some divs. I want to be able to drag that object. Only problem: I can't get it working.
I've found a couple of SO answers and tried to implement them. But none of them worked because i did get the same error over and over again:
Uncaught TypeError: undefined is not a function
or
evt is not defined
I've got to following code from this website (pointed out by an SO answer ):
<svg id="bewaar_holder" xmlns="http://www.w3.org/2000/svg" width="800" height="500" onload="Init(evt)" onmousedown="Grab(evt)" onmousemove="Drag(evt)" onmouseup="Drop(evt)"></svg>
var SVGDocument = null;
var SVGRoot = null;
var TrueCoords = null;
var GrabPoint = null;
var BackDrop = null;
var DragTarget = null;
function Init(evt) {
SVGDocument = evt.target.ownerDocument;
SVGRoot = SVGDocument.documentElement;
// these svg points hold x and y values...
// very handy, but they do not display on the screen (just so you know)
TrueCoords = SVGRoot.createSVGPoint();
GrabPoint = SVGRoot.createSVGPoint();
// this will serve as the canvas over which items are dragged.
// having the drag events occur on the mousemove over a backdrop
// (instead of the dragged element) prevents the dragged element
// from being inadvertantly dropped when the mouse is moved rapidly
BackDrop = SVGDocument.getElementById('BackDrop');
}
function Grab(evt) {
// find out which element we moused down on
var targetElement = evt.target;
// you cannot drag the background itself, so ignore any attempts to mouse down on it
if (BackDrop != targetElement) {
//set the item moused down on as the element to be dragged
DragTarget = targetElement;
// move this element to the "top" of the display, so it is (almost)
// always over other elements (exception: in this case, elements that are
// "in the folder" (children of the folder group) with only maintain
// hierarchy within that group
DragTarget.parentNode.appendChild(DragTarget);
// turn off all pointer events to the dragged element, this does 2 things:
// 1) allows us to drag text elements without selecting the text
// 2) allows us to find out where the dragged element is dropped (see Drop)
DragTarget.setAttributeNS(null, 'pointer-events', 'none');
// we need to find the current position and translation of the grabbed element,
// so that we only apply the differential between the current location
// and the new location
var transMatrix = DragTarget.getCTM();
GrabPoint.x = TrueCoords.x - Number(transMatrix.e);
GrabPoint.y = TrueCoords.y - Number(transMatrix.f);
}
};
function Drag(evt) {
// account for zooming and panning
GetTrueCoords(evt);
// if we don't currently have an element in tow, don't do anything
if (DragTarget) {
// account for the offset between the element's origin and the
// exact place we grabbed it... this way, the drag will look more natural
var newX = TrueCoords.x - GrabPoint.x;
var newY = TrueCoords.y - GrabPoint.y;
// apply a new tranform translation to the dragged element, to display
// it in its new location
DragTarget.setAttributeNS(null, 'transform', 'translate(' + newX + ',' + newY + ')');
}
};
function Drop(evt) {
// if we aren't currently dragging an element, don't do anything
if (DragTarget) {
// since the element currently being dragged has its pointer-events turned off,
// we are afforded the opportunity to find out the element it's being dropped on
var targetElement = evt.target;
// turn the pointer-events back on, so we can grab this item later
DragTarget.setAttributeNS(null, 'pointer-events', 'all');
if ('Folder' == targetElement.parentNode.id) {
// if the dragged element is dropped on an element that is a child
// of the folder group, it is inserted as a child of that group
targetElement.parentNode.appendChild(DragTarget);
alert(DragTarget.id + ' has been dropped into a folder, and has been inserted as a child of the containing group.');
} else {
// for this example, you cannot drag an item out of the folder once it's in there;
// however, you could just as easily do so here
alert(DragTarget.id + ' has been dropped on top of ' + targetElement.id);
}
// set the global variable to null, so nothing will be dragged until we
// grab the next element
DragTarget = null;
}
};
function GetTrueCoords(evt) {
// find the current zoom level and pan setting, and adjust the reported
// mouse position accordingly
var newScale = SVGRoot.currentScale;
var translation = SVGRoot.currentTranslate;
TrueCoords.x = (evt.clientX - translation.x) / newScale;
TrueCoords.y = (evt.clientY - translation.y) / newScale;
};
But i get this error:
Uncaught TypeError: undefined is not a function
on line: TrueCoords = SVGRoot.createSVGPoint();
Chrome debugger gives the following output when i log the evt
value:
And there's something i don't understand... In the first row srcElement
do got an value,but when i expand the event, it doesn't got an srcElement
anymore...
So i'm out of clues. I've never worked with svg before and i'm no JS guru. So i hope that you guys know what i do wrong...
Upvotes: 2
Views: 2968
Reputation: 13852
Not quite sure what its supposed to do, but this will get rid of the error...you can drag and drop, I'm just not sure what the backdrop is supposed to be, if its part of the same svg or something else.
The problem seemed to be that the SVGRoot was becoming the html element rather than svg (maybe because its on a fiddle, not sure), so I changed this to select by its ID.
SVGRoot = document.getElementById('bewaar_holder');
and
BackDrop = document.getElementById('BackDrop');
You will probably want to change these.
Upvotes: 3