Pass event from overlayed div to Highmaps - javascript

I'm using Highmaps to show a simple map and over it I have another div with a component that draws things on top of the map (this has to be done this way, I cannot draw everything inside Highmaps, unfortunely).
Now I would like to have some interation with the map, although it is below my div. I would like to know if there is a way to trigger the click, hover, etc. events on the map when I click on the div on top of it.
I have searched Highmaps docs trying to find a method a-like trigger(ev, x, y) but there seems to be none. Then I tried a pure javascript solution using MouseEvent() and dispatchEvent() on the Map DOM, but it also didn't work.
I have set up a simple fiddle with what I want to work: http://jsfiddle.net/k11yfz58/1/
If we coment the #overlay div, we get an alert when we click in a state. I want the same behavior but clicking on the overlay div, is that possible?
Thanks!
EDIT
It was suggested to use the pointer-events CSS property for this. That does not solve my problem because I also need the events to be triggered on the #overlay div.

You can add this to your css:
#overlay {
pointer-events: none;
}
From MDN:
The pointer-events CSS property specifies under what circumstances (if any) a particular graphic element can become the target of mouse events.
and the none value:
none
The element is never the target of mouse events; however, mouse events may target its descendant elements if those descendants have pointer-events set to some other value. In these circumstances, mouse events will trigger event listeners on this parent element as appropriate on their way to/from the descendant during the event capture/bubble phases.
What this means is that your overlay element is "invisible" to mouse events, and thus mouse events occur on the div below.

Related

Temporary alternative to .remove() in D3

I'm working on a project where I append a tooltip to the DOM. When I mouseover an object, the tooltip fades into view. When I leave, the tooltip fades out.
The problem I'm having is, it's still on top of other elements, and thus blocks their mouseover events from firing. Normally, I would just remove the element: d3.select("#theElement").transition().style("opacity",0).remove()
The problem is, I want to be able to re-append it later, upon mouseover. Is there a way to temporarily tack the tooltip somewhere it won't get in the way? Maybe down in the DOM or something? How do most people deal with this problem in D3?
If the issue is that you are blocking mouse over events under the tooltip when it is otherwise invisible, you could set the pointer-events property to none when you need to hide the element (rather than moving the element):
selection.style("pointer-events","none")
Mouse events will now see through this element and the tooltip won't block other "mouseover events from firing"
If the tooltip doesn't have mouse interaction at all, you could just append the tooltip with pointer-events set to none to start (or use css to achive the same result).
But, if the tooltip has mouse interaction, then you can re-set the pointer events attribute when you need mouse interaction on the tooltip again:
selection.style("pointer-events","all")
You have several options:
Set the element's CSS z-index to behind the other elements:
document.getElementById('theElement').style.zIndex = -9999;
Set the element's CSS style visibility to hidden:
document.getElementById('theElement').style.visibility = "hidden";
Edit: originally I suggested changing its display style, but apparently not-displayed still generates events.
Or set a CSS class that hides the element:
document.getElementById('theElement').className += ' hide';
where your stylesheet has:
.hide: { display: none; visibility: hidden }
or you are using an existing CSS framework that has this class (e.g. Bootstrap).
Should be able to accomplish similar with d3:
d3.select('#theElement').style('z-index', -9999);
d3.select('#theElement').style('visibility', 'hidden');
d3.select('#theElement').classed('hide', true);

Stop ng-mouseenter and ng-mouseout from triggering on inner elements

I have an unordered list where each li is a fixed small size but when you hover over it, it will expand to full size. This is done via ng-mouseover and ng-mouseout. The problem is that some of the li text contains other markup ( for example) and when the mouse enters the tag, it triggers a mouseout event and collapses the li.
Obviously the desired behavior is to have the li remain enlarged while the mouse is inside it, even if it's over a child element. Does anyone have an idea on how to basically ignore the mouseover of an inner element? I would also need to ignore the mouseout of the li if it were going into a child element.
There are two choices:
Use CSS pointer-events on your inner elements (but first check how well it is supported in your target browsers).
Use ngMouseenter/ngMouseleave instead (See this plnkr for different behavior between mouseenter/mouseleave vs. mouseover/mouseout)

Apply gradient over page without hindering user interaction [duplicate]

I have a div that has background:transparent, along with border. Underneath this div, I have more elements.
Currently, I'm able to click the underlying elements when I click outside of the overlay div. However, I'm unable to click the underlying elements when clicking directly on the overlay div.
I want to be able to click through this div so that I can click on the underlying elements.
Yes, you CAN do this.
Using pointer-events: none along with CSS conditional statements for IE11 (does not work in IE10 or below), you can get a cross browser compatible solution for this problem.
Using AlphaImageLoader, you can even put transparent .PNG/.GIFs in the overlay div and have clicks flow through to elements underneath.
CSS:
pointer-events: none;
background: url('your_transparent.png');
IE11 conditional:
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='your_transparent.png', sizingMethod='scale');
background: none !important;
Here is a basic example page with all the code.
Yes, you CAN force overlapping layers to pass through (ignore) click events.
PLUS you CAN have specific children excluded from this behavior...
You can do this, using pointer-events
pointer-events influences the reaction to click-, tap-, scroll- und hover events.
In a layer that should ignore / pass-through mentioned events you set
pointer-events: none;
Children of that unresponsive layer that need to react mouse / tap events again need:
pointer-events: auto;
That second part is very helpful if you work with multiple overlapping div layers (probably some parents being transparent), where you need to be able to click on child elements and only that child elements.
Example usage:
.parent {
pointer-events:none;
}
.child {
pointer-events:auto;
}
<div class="parent">
I'm unresponsive
I'm clickable again, wohoo !
</div>
Allowing the user to click through a div to the underlying element depends on the browser. All modern browsers, including Firefox, Chrome, Safari, and Opera, understand pointer-events:none.
For IE, it depends on the background. If the background is transparent, clickthrough works without you needing to do anything. On the other hand, for something like background:white; opacity:0; filter:Alpha(opacity=0);, IE needs manual event forwarding.
See a JSFiddle test and CanIUse pointer events.
I'm adding this answer because I didn’t see it here in full. I was able to do this using elementFromPoint. So basically:
attach a click to the div you want to be clicked through
hide it
determine what element the pointer is on
fire the click on the element there.
var range-selector= $("")
.css("position", "absolute").addClass("range-selector")
.appendTo("")
.click(function(e) {
_range-selector.hide();
$(document.elementFromPoint(e.clientX,e.clientY)).trigger("click");
});
In my case the overlaying div is absolutely positioned—I am not sure if this makes a difference. This works on IE8/9, Safari Chrome and Firefox at least.
Hide overlaying the element
Determine cursor coordinates
Get element on those coordinates
Trigger click on element
Show overlaying element again
$('#elementontop').click(e => {
$('#elementontop').hide();
$(document.elementFromPoint(e.clientX, e.clientY)).trigger("click");
$('#elementontop').show();
});
I needed to do this and decided to take this route:
$('.overlay').click(function(e){
var left = $(window).scrollLeft();
var top = $(window).scrollTop();
//hide the overlay for now so the document can find the underlying elements
$(this).css('display','none');
//use the current scroll position to deduct from the click position
$(document.elementFromPoint(e.pageX-left, e.pageY-top)).click();
//show the overlay again
$(this).css('display','block');
});
I currently work with canvas speech balloons. But because the balloon with the pointer is wrapped in a div, some links under it aren't click able anymore. I cant use extjs in this case.
See basic example for my speech balloon tutorial requires HTML5
So I decided to collect all link coordinates from inside the balloons in an array.
var clickarray=[];
function getcoo(thatdiv){
thatdiv.find(".link").each(function(){
var offset=$(this).offset();
clickarray.unshift([(offset.left),
(offset.top),
(offset.left+$(this).width()),
(offset.top+$(this).height()),
($(this).attr('name')),
1]);
});
}
I call this function on each (new) balloon. It grabs the coordinates of the left/top and right/down corners of a link.class - additionally the name attribute for what to do if someone clicks in that coordinates and I loved to set a 1 which means that it wasn't clicked jet. And unshift this array to the clickarray. You could use push too.
To work with that array:
$("body").click(function(event){
event.preventDefault();//if it is a a-tag
var x=event.pageX;
var y=event.pageY;
var job="";
for(var i in clickarray){
if(x>=clickarray[i][0] && x<=clickarray[i][2] && y>=clickarray[i][1] && y<=clickarray[i][3] && clickarray[i][5]==1){
job=clickarray[i][4];
clickarray[i][5]=0;//set to allready clicked
break;
}
}
if(job.length>0){
// --do some thing with the job --
}
});
This function proofs the coordinates of a body click event or whether it was already clicked and returns the name attribute. I think it is not necessary to go deeper, but you see it is not that complicate.
Hope in was enlish...
Another idea to try (situationally) would be to:
Put the content you want in a div;
Put the non-clicking overlay over the entire page with a z-index higher,
make another cropped copy of the original div
overlay and abs position the copy div in the same place as the original content you want to be clickable with an even higher z-index?
Any thoughts?
I think the event.stopPropagation(); should be mentioned here as well. Add this to the Click function of your button.
Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
Just wrap a tag around all the HTML extract, for example
<a href="/categories/1">
<img alt="test1" class="img-responsive" src="/assets/photo.jpg" />
<div class="caption bg-orange">
<h2>
test1
</h2>
</div>
</a>
in my example my caption class has hover effects, that with pointer-events:none; you just will lose
wrapping the content will keep your hover effects and you can click in all the picture, div included, regards!
An easier way would be to inline the transparent background image using Data URIs as follows:
.click-through {
pointer-events: none;
background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
}
I think that you can consider changing your markup. If I am not wrong, you'd like to put an invisible layer above the document and your invisible markup may be preceding your document image (is this correct?).
Instead, I propose that you put the invisible right after the document image but changing the position to absolute.
Notice that you need a parent element to have position: relative and then you will be able to use this idea. Otherwise your absolute layer will be placed just in the top left corner.
An absolute position element is positioned relative to the first parent
element that has a position other than static.
If no such element is found, the containing block is html
Hope this helps. See here for more information about CSS positioning.
You can place an AP overlay like...
#overlay {
position: absolute;
top: -79px;
left: -60px;
height: 80px;
width: 380px;
z-index: 2;
background: url(fake.gif);
}
<div id="overlay"></div>
just put it over where you dont want ie cliked. Works in all.
This is not a precise answer for the question but may help in finding a workaround for it.
I had an image I was hiding on page load and displaying when waiting on an AJAX call then hiding again however...
I found the only way to display my image when loading the page then make it disappear and be able to click things where the image was located before hiding it was to put the image into a DIV, make the size of the DIV 10x10 pixels or small enough to prevent it causing an issue then hiding the containing div. This allowed the image to overflow the div while visible and when the div was hidden, only the divs area was affected by inability to click objects beneath and not the whole size of the image the DIV contained and was displaying.
I tried all the methods to hide the image including CSS display=none/block, opacity=0, hiding the image with hidden=true. All of them resulted in my image being hidden but the area where it was displayed to act like there was a cover over the stuff underneath so clicks and so on wouldn't act on the underlying objects. Once the image was inside a tiny DIV and I hid the tiny DIV, the entire area occupied by the image was clear and only the tiny area under the DIV I hid was affected but as I made it small enough (10x10 pixels), the issue was fixed (sort of).
I found this to be a dirty workaround for what should be a simple issue but I was not able to find any way to hide the object in its native format without a container. My object was in the form of etc. If anyone has a better way, please let me know.
I couldn't always use pointer-events: none in my scenario, because I wanted both the overlay and the underlying element(s) to be clickable / selectable.
The DOM structure looked like this:
<div id="outerElement">
<div id="canvas-wrapper">
<canvas id="overlay"></canvas>
</div>
<!-- Omitted: element(s) behind canvas that should still be selectable -->
</div>
(The outerElement, canvas-wrapper and canvas elements have the same size.)
To make the elements behind the canvas act normally (e.g. selectable, editable), I used the following code:
canvasWrapper.style.pointerEvents = 'none';
outerElement.addEventListener('mousedown', event => {
const clickedOnElementInCanvas = yourCheck // TODO: check if the event *would* click a canvas element.
if (!clickedOnElementInCanvas) {
// if necessary, add logic to deselect your canvas elements ...
wrapper.style.pointerEvents = 'none';
return true;
}
// Check if we emitted the event ourselves (avoid endless loop)
if (event.isTrusted) {
// Manually forward element to the canvas
const mouseEvent = new MouseEvent(event.type, event);
canvas.dispatchEvent(mouseEvent);
mouseEvent.stopPropagation();
}
return true;
});
Some canvas objects also came with input fields, so I had to allow keyboard events, too.
To do this, I had to update the pointerEvents property based on whether a canvas input field was currently focused or not:
onCanvasModified(canvas, () => {
const inputFieldInCanvasActive = // TODO: Check if an input field of the canvas is active.
wrapper.style.pointerEvents = inputFieldInCanvasActive ? 'auto' : 'none';
});
it doesn't work that way. the work around is to manually check the coordinates of the mouse click against the area occupied by each element.
area occupied by an element can found found by 1. getting the location of the element with respect to the top left of the page, and 2. the width and the height. a library like jQuery makes this pretty simple, although it can be done in plain js. adding an event handler for mousemove on the document object will provide continuous updates of the mouse position from the top and left of the page. deciding if the mouse is over any given object consists of checking if the mouse position is between the left, right, top and bottom edges of an element.
Nope, you can't click ‘through’ an element. You can get the co-ordinates of the click and try to work out what element was underneath the clicked element, but this is really tedious for browsers that don't have document.elementFromPoint. Then you still have to emulate the default action of clicking, which isn't necessarily trivial depending on what elements you have under there.
Since you've got a fully-transparent window area, you'll probably be better off implementing it as separate border elements around the outside, leaving the centre area free of obstruction so you can really just click straight through.

Jquery Mouseenter triggering parent elements

I am trying to create a firebug type rollover element selector, but seem to be having problems with the rollover triggering parent elements that contain my target element.
See the following example:
http://jsbin.com/elofe3/edit
There are 3 divs on the page, all with mouseenter/mouseleave listeners, the largest is totally independent of theother two, the second largest is position ontop of the largest but is not contained within it, and the sallest is nested within the second largest, (it's parent). It may be easier to visualise if you look at the source.
If you click preview and roll you mouse over the central div, you will notice that the second largest div also continues to respond to the mouseenter event and remains outlined in red. To fix this, I tried to add $(this).parent().trigger("mouseout"); on each rollover listener.
http://jsbin.com/elofe3/4/edit
But them when your mouse leaves the smallest (pink) div, to the middle (black) div, the middle div does not fire (presumably because mouseenter/mouseover is not being fired as the mouse has never actually left the central div.
I understand that in this situation I could just add $(this).parent().trigger("mouseover"); to the mouseleave listener on each div, but it would'nt work in evey example, (for instance, a div nested within its parent, but is positioned outside of that parent on the page.)
I require some novel solution to this, it needs to work very similarly to the firebug, element selector (the tool that lets you rollover elements on the page (higlighting them) and click to select them and triggering it to show the source for that element).
Any help greatly appreciated.
This is how mouseenter and mouseleave work. But you are mislead, mouseenter is not triggered on the parent element. Instead, mouseleave is not triggered if you hover over descendants. So it is not that the border is added again, but that it is never removed.
Add the event handlers to mouseover and mouseout and prevent the event from bubbling up:
$("div").mouseover(function(e) {
e.stopPropagation();
$(this).css("outline", "solid 3px red");
});
$("div").mouseout(function(e) {
e.stopPropagation();
$(this).css("outline", "none");
});
http://jsbin.com/elofe3/5/edit

Pass mouse events through absolutely-positioned element

I'm attempting to capture mouse events on an element with another absolutely-positioned element on top of it.
Right now, events on the absolutely-positioned element hit it and bubble up to its parent, but I want it to be "transparent" to these mouse events and forward them on to whatever is behind it. How should I implement this?
pointer-events: none;
Is a CSS property that makes events "pass through" the HTML-element to which the property is applied. It makes the event occur on the element "below".
See for details: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
It is supported by almost all browsers, including IE11; global support was ~98.2% in 05/'21): http://caniuse.com/#feat=pointer-events (thanks to #s4y for providing the link in the comments).
Also nice to know...
Pointer-events can be disabled for a parent element (probably transparent div) and yet be enabled for child elements.
This is helpful if you work with multiple overlapping div layers, where you want to be able click the child elements of any layer. For this all parenting divs get pointer-events: none and click-children get pointer-events reenabled by pointer-events: all
.parent {
pointer-events:none;
}
.child {
pointer-events:all;
}
<div class="some-container">
<ul class="layer-0 parent">
<li class="click-me child"></li>
<li class="click-me child"></li>
</ul>
<ul class="layer-1 parent">
<li class="click-me-also child"></li>
<li class="click-me-also child"></li>
</ul>
</div>
If all you need is mousedown, you may be able to make do with the document.elementFromPoint method, by:
removing the top layer on mousedown,
passing the x and y coordinates from the event to the document.elementFromPoint method to get the element underneath, and then
restoring the top layer.
The reason you are not receiving the event is because the absolutely positioned element is not a child of the element you are wanting to "click" (blue div). The cleanest way I can think of is to put the absolute element as a child of the one you want clicked, but I'm assuming you can't do that or you wouldn't have posted this question here :)
Another option would be to register a click event handler for the absolute element and call the click handler for the blue div, causing them both to flash.
Due to the way events bubble up through the DOM I'm not sure there is a simpler answer for you, but I'm very curious if anyone else has any tricks I don't know about!
There is a javascript version available which manually redirects events from one div to another.
I cleaned it up and made it into a jQuery plugin.
Here's the Github repository:
https://github.com/BaronVonSmeaton/jquery.forwardevents
Unfortunately, the purpose I was using it for - overlaying a mask over Google Maps did not capture click and drag events, and the mouse cursor does not change which degrades the user experience enough that I just decided to hide the mask under IE and Opera - the two browsers which dont support pointer events.
If you know the elements that need mouse events, and if your overlay is transparent, you can just set the z-index of them to something higher than the overlay. All events should of course work in that case on all browsers.

Categories

Resources