Click on SVG image (JS) - javascript

I have some SVG images on my html page. I want get id of each image by click on it.
HTML:
<div class = "svgLoading"><object data="images/91.svg" type="image/svg+xml" id = "47"></object></div>
<div class = "svgPoints"><object data="images/30.svg" type="image/svg+xml" id = "48"></object></div>
I'm trying to get them like this:
document.body.onclick = function(event) {
var target = event.target || event.srcElement;
alert(target.tagName); }
Its working fine for other elements, for example: img, div without object.
But clicking on the svg is ignored. Why is this happening and how to properly handle clicks on svg?

The tag can not be clicked but you can make a workaround and set the click action to parent. ( Take a look at this SO question )
Set the object pointer-events to none, in order to make the element to be never the target of pointer events:
The element is never the target of pointer events; however, pointer events may target its descendant elements if those descendants have pointer-events set to some other value. In these circumstances, pointer events will trigger event listeners on this parent element
$("#svg_47").click(function(target) {
var target = event.target || event.srcElement;
console.log("object id: " + target.firstElementChild.id)
alert(target.tagName);
});
object {
width: 100%;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="svgLoading" id="svg_47">
<object data="https://css-tricks.com/wp-content/uploads/2015/05/kiwi.svg" type="image/svg+xml" id="47"></object>
</div>
<div class="svgPoints" id="svg_87">
<object data="https://svgsilh.com/svg/1801287.svg" type="image/svg+xml" id="48"></object>
</div>

Related

How to specify with multiple objects in JS?

So I'm trying to make a code that allows me to change the image once I hover over it.
The initial code works. But I have 72 other images to go through with this feature. I'm trying to call each one individually so I don't have to repeat so much code.
I want a simple html code like
<img id="seal" src="img/seal/dantalion.png" onmouseover="rollover(dantalion)"
onmouseout="rollaway(dantalion)" />
<img id="seal" src="img/seal/vassago.png" onmouseover="rollover(vassago)"
onmouseout="rollaway(vassago)" />
Here is the code that works.
function rollover(img) {img.src = "img/seal/hover/vassago.png";}
function rollaway(img) {img.src = "img/seal/vassago.png";}
Here is what I want to do. Keep in mind please, I'm new to this sort of thing.
function rollover() {
dantalion.src = "img/seal/hover/dantalion.png";
vassago.src = "img/seal/hover/vassago.png";
}
function rollaway() {
dantalion.src = "img/seal/dantalion.png";
vassago.src = "img/seal/vassago.png";
}
How do I individually call the object in the HTML code?
This is a simple solution to your problem. On mouseover of the wrapper div #images we check if you are hovering an image and if so, update the image src with the hover url.
On mouseout or if you hover on a different image the images are reset back to the original src
var images = document.getElementById('images');
var prevEl;
function resetImages() {
if (prevEl) {
prevEl.src = prevEl.src.replace('/hover', '');
prevEl = null;
}
}
images.addEventListener('mouseover', function(e) {
resetImages();
if (e.target && e.target.nodeName == "IMG") {
prevEl = e.target;
e.target.src = e.target.src.replace('/seal', '/seal/hover');
}
});
images.addEventListener('mouseout', resetImages);
<div id="images">
<img src="img/seal/dantalion.png">
<img src="img/seal/vassago.png">
</div>
Here's an example using event delegation. I've tried to use only core JS APIs because you didn't mention any libraries but if you were using a library there's a good chance it would be able to do some of this for you.
document.body.addEventListener('mouseover', function(ev) {
var target = ev.target,
cls = target.classList;
if (!cls.contains('seal')) {
return;
}
cls.add('seal-over');
target.innerHTML = target.innerHTML.replace('/seal/', '/seal/hover/');
});
document.body.addEventListener('mouseout', function(ev) {
var target = ev.target,
cls = target.classList;
if (!cls.contains('seal')) {
return;
}
cls.remove('seal-over');
target.innerHTML = target.innerHTML.replace('/hover', '');
});
.seal {
border: 1px dotted #777;
height: 70px;
margin: 10px;
width: 200px;
}
.seal-over {
background: #eee;
}
<div class="seal">img/seal/dantalion.png</div>
<div class="seal">img/seal/vassago.png</div>
While my example changes the innerHTML you would change the src instead - I didn't have your images so I couldn't easily use img tags. The id attribute has to be unique so I've changed it to using a class instead. Adding the seal-over class isn't required, I just thought it made the example more interesting: you'd probably use a CSS :hover pseudo-class for that if this were real code.
One of many guides to delegation, you can find others with a quick search online:
https://davidwalsh.name/event-delegate
The idea is to add a single listener on an element higher up the tree. Events propagate up so you can react to events on descendent elements. The event target refers to the element on which the actual event occurred. The code checks whether this element is one of the elements we care about and then makes the changes accordingly.

Drag-and-drop to div behind absolutely positioned div

I am doing drag and drop the HTML5-way (i.e. no jQuery draggable/droppable). I have a drop target (a div) that is partially obscured by another, absolutely positioned div, like so:
<div id="drop-target" style="width:100%; height:500px; background-color:blue;" />
<div id="obscuring-div" style="width:40%; height:150px; position:absolute; top:10px; left: 10px; background-color:red;" />
When I drop items on the absolutely positioned obscuring-div, is there any way to make the drop event trigger on drop-target instead of obscuring-div?
The comments from Pete led me to a javascript solution for forwarding click events through layers, and I could do something similar for drop events. For future reference, here's the code:
var element = $('#drop-target');
// This element should be droppable
element.on('dragover', function (event) {
event.preventDefault();
});
// This element should be droppable
element.on('dragenter', function (event) {
event.preventDefault();
});
element.on('drop', function (event) {
// Find position of drop event
var xPos = event.originalEvent.clientX,
yPos = event.originalEvent.clientY;
// Temporarily hide this element
element.hide();
// Find the element that is the real drop target
var dropTargetBelow = $(document.elementFromPoint(xPos, yPos));
// Trigger the drop event on real drop target
dropTargetBelow.trigger(event);
// Show this element again
$(this).show();
});
This also works when the elements are stacked, for example if there are three obscuring divs in front of the drop target.
Just for the record: a similar approach using the elementsFromPoint method and the native dispatchEvent:
someHandler(event): {
let elements = document.elementsFromPoint(event.clientX, event.clientY);
let target = elements.find(e => e.matches('#obscuring-div'));
target.dispatchEvent(new DragEvent('drop', {
// anything you need to pass; works without that in the simplest case
}));
}

Changing content of a div with Prototype or JavaScript

I have this code :
<div class="box_container">
<div class="box_container_button" id="navigator_1">
Button 1
</div>
<div class="box_container_button" id="navigator_2">
Button 2
</div>
<div class="box_container_button" id="navigator_3">
Button 3
</div>
<div class="box_container_content" style="background-color:#d5d5d5;" id="navigator_content_1">
Content 1
</div>
<div class="box_container_content" style="background-color:#00aeef; display:none;" id="navigator_content_2">
Content 2
</div>
<div class="box_container_content" style="background-color:#4db848; display:none;" id="navigator_content_3">
Content 3
</div>
</div>
If I press on the button with navigator_2, navigator_content_1 must be hidden, and navigator_content_2 showed.
How can I do this with prototype? (Or javascript if it's too stronger). Unfortunatly I can't use jQuery.
Try this
function nav(obj)
{
document.getElementById("navigator_content_1").style.display = "hidden"
document.getElementById("navigator_content_2").style.display = "hidden"
document.getElementById("navigator_content_3").style.display = "hidden"
obj.style.display = "none";
}
Add onclick="nav(this)" to each button element.
Here is my suggestion:
Give the container holding the buttons in ID (for convenience).
Change the IDs of the content containers from navigator_content_1 to navigator_1_content (again, for convenience).
Then all you have to do is to keep a reference to the currently showed content pane and you have to attach a click handler to the container holding the buttons:
// by default, the first panel is shown
var current = document.getElementById('navigator_1_content');
document.getElementById('box_container').onclick = function(event) {
event = event || window.event; // for IE
var target = event.target || event.srcElement; // for IE
current.style.display = 'none';
current = document.getElementById(target.id + '_content');
current.style.display = 'block';
};
This makes use of event bubbling. event.target has a reference to the element that was actually clicked (I don't know if the Safari bug is still present, you might have to traverse the DOM up to find the correct element). This can certainly be improved but should give you a good start. You can easily add new buttons / content panels without having to modify the code.
Here is a DEMO.
To learn more about event handling, I suggest to have a look at the excellent articles at quirksmode.org
This would use prototype and get what you want
$$('.box_container_button').each(function(element) {
element.observe('click', function(event) {
$$('.box_container_content').each(function(element) {
element.setStyle({
'display': 'none'
});
});
$('navigator_content_' + this.id.replace("navigator_", "")).setStyle({
'display': 'block'
});
});
});
http://jsfiddle.net/VENLh/
THIS solution would work even if you add more buttons / contents without changing any line in the javascript (just add the html part!)

if mouseout of one element == true && mouseover of the other element != true

If the dom contains the following nodes:
<span id="one_element"></span>
<div id="the_other_element"></div>
how to call some function in the event when the mouse cursor is out of the one element but not over the other element? Is there a method?
(the one element may be either block-level or inline-level, positioned static, relative, or absolute; the other element is positioned absolute in relation to the document and may overlay the one element.)
If you are not using any JS Library you could use global state variables like this:
<script>
var theOtherElementCursor = false;
function myEvent() {
if (!theOtherElementCursor) {
// Do something here!
}
}
</script>
.
.
.
<span id="one_element" onMouseOut="myEvent()"></span>
<div id="the_other_element"
onMouseOver="theOtherElementCursor = true;"
onMouseOut="theOtherElementCursor = false;"></div>

Problems with mouseout event

I'm using JavaScript to hide an image and show some text thats hidden under it. But, when the text is shown if you scroll over it, it fires the mouseout event on the container, that then hides the text and shows the image again, and it just goes into a weird loop.
The html looks like this:
<div onmouseover="jsHoverIn('1')"
onmouseout="jsHoverOut('1')">
<div id="image1" />
<div id="text1" style="display: none;">
<p>some content</p>
<p>some more content</p>
</div>
</div>
And the javascript (It uses scriptaculous):
function jsHoverIn(id) {
if(!visible[id]) {
new Effect.Fade ("image" + id, {queue: { position: 'end', scope: id } });
new Effect.Appear ("text" + id, {queue: { position: 'end', scope: id } });
visible[id] = true;
}
}
function jsHoverOut (id) {
var scope = Effect.Queues.get(id);
scope.each(function(effect) { effect.cancel(); });
new Effect.Fade ("text" + id, {queue: { position: 'end', scope: id } });
new Effect.Appear ("image" + id, {queue: { position: 'end', scope: id } });
visible[id] = false;
}
This seems really simple, but i just cant wrap my head around it.
I'd give the container div:
position: relative;
and add a third div in the container (should be the last child of the container) with:
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
and catch the mouseover and mouseout events on this div instead.
Because it has no child elements, you shouldn't get spurious mouseover and mouseout events propagating to it.
Edit:
What I believe happens, is that when the cursor moves from a parent element onto a child element, a mouseout event occurs on the parent element, and a mouseover event occurs on the child element. However, if the mouseover handler on the child element does not catch the event and stop it propagating, the parent element will also receive the mouseover event.
It sounds like what you really want is mouseenter/mouseleave (IE proprietary events, but easy to emulate):
// Observe mouseEnterLeave on mouseover/mouseout
var mouseEnterLeave = function(e) {
var rel = e.relatedTarget, cur = e.currentTarget;
if (rel && rel.nodeType == 3) {
rel = rel.parentNode;
}
if(
// Outside window
rel == undefined ||
// Firefox/other XUL app chrome
(rel.tagName && rel.tagName.match(/^xul\:/i)) ||
// Some external element
(rel && rel != cur && rel.descendantOf && !rel.descendantOf(cur))
) {
e.currentTarget.fire('mouse:' + this, e);
return;
}
};
$(yourDiv).observe('mouseover', mouseEnterLeave.bind('enter'));
$(yourDiv).observe('mouseout', mouseEnterLeave.bind('leave'));
// Use mouse:enter and mouse:leave for your events
$(yourDiv).observe(!!Prototype.Browser.IE ? 'mouseenter' : 'mouse:enter', yourObserver);
$(yourDiv).observe(!!Prototype.Browser.IE ? 'mouseleave' : 'mouse:leave', yourObserver);
Alternatively, patch prototype.js and use mouseenter and mouseleave with confidence. Note that I've expanded the check for leaving the window or entering XUL chrome; this seemed to fix some edge cases in Firefox for me.
Shouldn't the onmouseover event be on the image div and the onmouseout event be on the text div?
I'm not sure if this would fit with the rest of your styling, but perhaps if you changed the css on the text div so it was the same size as the image, or fixed the size of the outer div, then when the mouseover event fired, the size of the outer div wouldn't change so much as to cause the mouseout event.
Does this make sense?
This may not be the best solution but you could set a global boolean variable that would be accessible to both methods that would just specify if the last action was HoverIn or HoverOut. You could use this boolean variable to determine if the code should run or not.
if (bWasHoverIn){
...
}
Try using
onmouseenter instead of onmouseover and
onmouseleave instead of onmouseout.

Categories

Resources