mouse events on overlapping SVG elements - javascript

How to handle click events on overlapping svg elements? I am using Reaphael.js library.
Problem is that top <svg> DOM element is intercepting mouse event, even if bottom element is not overlapped by any drawing. My question is how to make both circles in my example clickable and still keep them in two svg elemnts?
Here is my code:
CSS:
#container {
position: relative;
width: 200px;
height: 200px
}
#container>svg {
position: absolute !important;
top: 0;
left: 0;
}
JavaScript:
var topLayer = Raphael('container', 200, 200);
var bottomLayer = Raphael('container', 200, 200);
topLayer.circle(100, 100, 50)
.attr({
fill: 'red',
stroke: false
})
.mousedown(function(){alert('Top layer')});
bottomLayer.circle(120, 120, 50)
.attr({
fill: 'pink',
stroke: false
})
.mousedown(function(){alert('Bottom layer')});
Working JSFiddle example
PS: I know that I can achieve layering in single <svg> DOM elemnt, but this is not a case. My bottom SVG element have zoom and pan capabilities, while top SVG element should be static.

You can manipulate css property called pointer-events by setting it to none. This will result in allowing all events flowing through the layer that is marked with pointer-events:none.
You can see this fiddle: https://jsfiddle.net/krul/6SmQ9/2/
#container {
pointer-events:none;
position: relative;
width: 200px;
height: 200px
}
#container>svg {
pointer-events:none;
position: absolute !important;
top: 0;
left: 0;
}
svg circle {
pointer-events:visible;
}

Related

Centre Lottie SVG to a Header element

I have an animation from Lottiefiles (in JSON format), which is then converted into an animated SVG document by the Lottie framwork. However, I can't seem to position the SVG document with the header tag. I'd like it to be beside the text.
I tried to search existing threads for similar things, but none of these worked, except for one (sort of). This included adding the SVG into a div, inside the header itself. However, when I tried this, the SVG document is fixed in place, so while it worked for shorter text (less than 6 characters), if the text was longer, the SVG would appear underneath, instead of moving to the end of the text.
I also have to manually assign the style to the SVG file through Javascript in a timeout, because the SVG document doesn't exist initially.
This is the actual header code (in PugJS).
h1(class="channel-header" style="margin-bottom: 36px; width: 96px; margin: auto;") #{channel}
if premium
div(id="bodyanim" class="badge baseline")
Here is the SASS for the header and inner div tag:
.badge
display: inline-flex
align-self: center
height: 70%
.badge svg, .badge img
height: 1em
width: 1em
fill: currentColor
z-index: -1
position: absolute
left: 0
top: 0
.badge.baseline svg, .badge img
top: .125em
position: relative
.channel-header
margin: 0 0 16px 0
padding: 0
line-height: 1
font-weight: normal
position: relative
height: 45px
And here's the JS setting the SVG object, and setting its CSS after a timeout.
var animData = {
wrapper: document.getElementById('bodyanim'),
animType: 'svg',
loop: true,
prerender: true,
autoplay: true,
path: '/anims/4768-trophy.json'
};
var anim = bodymovin.loadAnimation(animData);
setTimeout(function () {
var svg = animData.wrapper.getElementsByTagName("svg")[0];
svg.style.position = "absolute";
svg.style.left = "0";
svg.style.top = "0";
svg.style.zIndex = -1;
svg.style.marginLeft = "65%";
}, 100);
When I run the site with this code, the header works for any text shorter than 7 characters, but if there's more, the header tries to "push" itself above the SVG document, and the SVG remains in position behind it instead of moving along with the text.
You can see an example of this on this site (you can either edit the endpoint, i.e. /channel/anythinghere or edit the tag client-side):
http://themadgamers.co.uk:3000/channel/ItsMike
Why do you set a fixed width for your h1?
h1(class="channel-header" style="margin-bottom: 36px; width: 96px; margin: auto;")
If you remove the 96px width restriction, longer strings no longer push the trophy below the user names.
As for the manual need to style the SVG via JavaScript...
setTimeout(function () {
var svg = animData.wrapper.getElementsByTagName("svg")[0];
svg.style.position = "absolute";
svg.style.left = "0";
svg.style.top = "0";
svg.style.zIndex = -1;
svg.style.marginLeft = "65%";
}, 100);
Consider adding a new class to your CSS.
.mySvg {
position: absolute;
left: 0;
top: 0;
z-index: -1;
margin-left: 65%;
}
Then you should be able to simplify the JavaScript to:
setTimeout(function () {
var svg = animData.wrapper.getElementsByTagName("svg")[0];
svg.className = "mySvg";
}, 100);

How to make tooltip display beside every image

I have several images that I applied jQuery tooltip effect on..it works, but the problem is that irrespective of the image i click the tooltip only displays at the top of the page.
Is there a way I can make the tooltip appear beside each image that is clicked.
The jQuery I have so far looks like this :
$('.image').hover(function () {
$('.tooltip').fadeIn(1000);
}, function () {
$('.tooltip').fadeOut(1000);
});
$('.tooltip').css({
top: e.pageY,
left: e.pageX
})
CSS
.tooltip {
display: none;
height: auto;
position: absolute;
background-color: #D1C585;
color: black;
border: 2px solid rgba(128, 0, 32, 0.3);
padding: 5px;
border-radius: 5px;
font-family: actor;
}
HTML
<img src="image.jpg" class="image">
It needs to have position: absolute and its parent has to be position: relative. Inside the hover function do:
$('.tooltip').css({
top: e.pageY,
left: e.pageX
})
It all depends on where the location of the tooltip node is located. However, you can get the fixed position of the image by grabbing and adding all the offsetLeft/offsetTop of all the element and it's parents. For example.
$('.image').hover(function() {
var e = this,top=0,left=0;
while(e){
top+=e.offsetTop;
left+=e.offsetLeft;
e=$(e).parent();
}
$(this).css({position:'fixed',top:top+"px",left:left:left+"px"});
$('.tooltip').fadeIn(1000);
}, function() {
$('.tooltip').fadeOut(1000);
});
I did not test this code. However, I hope this points you in the right direction. You can then use the size of the image(offsetHeight and offsetWidth) to position the tooltip around the image.
Here is an example using jquery ui, see jquery ui tooltip for documentation
HTML
<img src="http://img262.imageshack.us/img262/68/sahara4rs.jpg" title="tyre">
Javascript
$(document).tooltip({
track: true,
show: {
effect: "fade",
delay: 1000
},
hide: {
effect: "explode",
delay: 250
}
});
On jsfiddle

Multiple line jquery tooltip

How to make custom jquery tooltip appear as multiple line that adjusts to fixed width? So it not go in one long line (if 'title' attribute is very long). Because Now if I write long 'title' attribute, tooltip is displayed on one long line and it does not matter what width is set to tooltip element.
My code to get better understanding of what I'm asking:
http://jsfiddle.net/8XttH/
jquery code:
$(document).ready(function() {
$("body").append("<div class='tip'></div>");
$("p[title]").each(function() {
$(this).hover(function(e) {
$().mousemove(function(e) {
var tipY = e.pageY + 16;
var tipX = e.pageX + 16;
$(".tip").css({'top': tipY, 'left': tipX});
});
$(".tip")
.html($(this).attr('title'))
.stop(true,true)
.fadeIn("fast");
$(this).removeAttr('title');
}, function() {
$(".tip")
.stop(true,true)
.fadeOut("fast");
$(this).attr('title', $(".tip").html());
});
});
});
Set a max-width on the tool tip box?
max-width: 100px;
Also set the height to auto so it increases as needed
height: auto;
The text will then wrap to the next line.
See this fiddle
Use this css
div.tip{
position: absolute;
top: 100px;
left: 100px;
width:100px;
border: 2px solid #FF0000;
background-color: #FF9999;
display: none;
padding: 3px;
}
Fiddle: http://jsfiddle.net/8XttH/2/

How do I dynamically change the css of an element that runs offscreen to make it stay onscreen?

I have what is for all intents a mouseover tooltip. It lives on multiple page elements (dynamically generated, so I never know how many there will be or what their positions are.)
I've had complaints that on lower-resolution screens, the tooltips on items in the rightmost column of elements run offscreen. Since I don't know the position of the parent item when it's created, I need a way to detect (before the mouseover actually happens) that the tooltip div will partially be offscreen when displayed, and change the css accordingly.
I know what the css needs to be; what I'm having trouble with is the detecting part. I've seen a few questions that are similar, but the solutions all involve using prototype or jquery plugins. I'm limited to core jquery (or just plain javascript) on this project.
Any pointers out there?
Here is a quick demo I put together on jsFiddle: http://jsfiddle.net/2gGrd/
HTML:
<p class="left">Left</p>
<p class="center">Center</p>
<p class="right">Right</p>​
CSS:
p {
position: absolute;
top: 50%;
}
.left {
left: 0;
}
.center {
left: 50%;
}
.right {
right: 0;
}
.toolTip {
width: 100px;
height: 25px;
background: red;
color: green;
position: absolute;
}
JavaScript:
var tip;
$('p').hover(function() {
$(this).css('color', 'red');
var xpos = $(this).width() / 2 + $(this).offset().left;
var ypos = $(this).height() / 2 + $(this).offset().top;
tip = createToolTip('thing', xpos, ypos);
$(this).parent().append(tip);
tip.offset({
left: tip.offset().left - tip.width() / 2
});
if (tip.offset().left < 0) tip.offset({
left: 0
});
if (tip.offset().left + tip.width() > $('body').width()) {
tip.offset({
left: $('body').width() - (tip.width())
});
}
}, function() {
$(this).css('color', '');
$(tip).remove();
});
function createToolTip(text, x, y) {
return $('<div />').addClass('toolTip').css('left', x).css('top', y).text(text);
}​
It's not perfect code, nor is it the same idea as you have for the tool tips, but hopefully it answers the question about keeping the items on screen.

How do I include an element's margin in the hot-spot for jQuery's hover() event?

jQuery(".my_container").hover(function(){
//do code
}, function(){
//do code
});
.my_container { width: 100px; height: 100px; margin: 50px; }
The code above doesn't react to mouse over of margin (margin isn't a part of element?) - how can I change that?
You could use a 50px transparent border instead - the margin isn't really supposed to be mouseable...
Include a pseudo element, e.g.
.my_container:before {
content:'';
position:absolute;
top:-50px;
bottom:-50px;
left:-50px;
right:-50px;
}
This adds an extra 50px to the existing element's clickable area.
If you only want to add this on touch screen devices, you could do this:
.touchevents .my_container:before {
...
}
This requires something like Modernizer to insert the appropriate feature-based CSS class.
Update
As per #Jaladh's comments, you may also need to apply position:relative to the container element, since position:absolute above will be relative to the first ancestor with a position attribute:
.my_container {
position:relative;
}
Perhaps use a 2nd wrapper element with padding on the outer element and existing background and padding styles on the inner element:
<div class="my_container">
<div class="my_container_inner">
<!-- etc. -->
</div>
</div>
jQuery(".my_container").hover(function(){
//do code
}, function(){
//do code
});
.my_container { padding: 50px; }
.my_container_inner { width: 100px; height: 100px; /* etc. */ }
Building upon #Dunc's solution, you can alternatively use pseudo element to mimic your container and let actual container behave like margins. This will look like:
.my_container {
width: calc(100px + (2 * 50px));
height: calc(100px + (2* 50px));
position: relative;
}
.my_container::before {
content: '';
position: absolute;
top: 50px;
bottom: 50px;
left: 50px;
right: 50px;
}
Also make sure to move all other properties (like background color, border, etc.) you had in my_container to my_container::before because before is acting like our container here.
This is essentially helpful if your containers are grid items and you want gaps in-between them to be hoverable, because otherwise using psuedo element to add margins won't work appropriately in that case.
Change the margin to padding and it'll be hoverable.

Categories