How to implement "mouse resistance" in JavaScript? - javascript

I need to implement mouse resistance in JavaScript.
As an example of what I mean, think of how the Enlightenment window manager handles screen edge resistance to switch between different desktops, or if you are not familiar with that:
Imagine a large rectangle with a square within it.
When click-moving the mouse [onmousedown] within the square, the mouse lets itself be moved until the borders of the square, then exercises some resistance until a threshold is met, and then moves around within the larger rectangle.
Ideally the mouse cursor should stay trapped within the square until that threshold isn't met, and only leave that area if it is met.
Any ideas or examples of this somewhere?
A cross-browser solution is also greatly appreciated. (Down to IE7, that is)

As stated you can't set the mouse position with Javascript.
Since you asked about implementing this on mousedown, however, I assume the user is dragging something around on screen. So you could have the element they are dragging show this behavior. You need two elements to act as regions, one where the target can be freely dragged and another to define the size of the boundary. I'd do it with jQuery to shorten up the code but basically you'd have something like this. (Untested code)
HTML:
<div class='borderLand'>
<div class='freeZone'>
<img class='draggable'>
</div>
</div>
CSS:
.borderLand {position: relative; width: 110px; height: 110px;}
.freeZone {position: relative; top: 10px; left:10px; height: 100px; width: 100px;}
JS:
I can't write the full code off the top of my head but the algorithm would be something like
onmousedown{
check for click location
if it's over the draggable (watch for bubbling) begin dragging, set dragging flag
}
onmouseup{
clear dragging flag if it's set
}
borderland onmouseover{
if dragging, stop the movement of the draggable (watch for bubbling here too)
}
borderland onmouseout{
start dragging again (if they move back in or out it doesn't matter, you want to drag)
}
Sorry if you need more detail, but doing this in plain JS would be a little lengthy and I'm not sure how much help you need.

Related

Javascript: Child should not be inheriting mouse state from Parent

I'm trying to create a small image that follows the mouse around but only exists inside a specific area. I'm using javascript/jquery to create the image when the mouse enters the area and remove it when the mouse leaves.
The problem is, if I create the "follower" inside the area div, the image seems to be considered part of it's parent for determining mouse state, and thus it continues to exist even after the mouse is outside the area.
(If I move the mouse fast enough the cursor will escape and the follower disapears.)
Here is the code I'm using:
$("#area").mouseenter(function() {
$("#area").append("<img id='follower' src='follower.png'/>");
});
$("#area").mousemove(function(event){
$("#follower").css("top",event.pageY-35);
$("#follower").css("left",event.pageX-35);
});
$("#area").mouseleave(function() {
$("#follower").remove();
});
JSFiddle: http://jsfiddle.net/cgWdF/186/
I have also attempted creating the "follower" inside a separate div, which works but results in a weird flickering of the image, as seen here: http://jsfiddle.net/cgWdF/187/
Any help, with this, would be appreciated. It doesn't matter whether the follower is created inside the area div, or not, as long as the flickering affect isn't seen. Also, I'd like to keep the code as compact as possible, but I'll take what I can get.
This occurs because the element on which you mouseleave is not the one on which you think it happens. In fact, your sprite is triggering the event instead because your pointer is over it at that time.
To prevent that from happening, you can force the page to cancel all pointer events on your sprite. By doing that, #area will trigger your pointer events as intended. The css rule pointer-events might be helpful for this.
CSS
#follower {
position: absolute;
height: 80px;
pointer-events: none;
}
There are probably better ways to deal with that but it's the most simple I can come up with for now.
Hope this helps!
See FIDDLE.

Trigger if user scrolls with scroll wheel when no scrollbar is present

So, I'm trying to create something that's unique and uses absolute positioning. Along with overflow:hidden;, this website wont contain a scrollbar, but I still need to test when the user is using the scroll wheel.
Attempts
Here is a Code Snippet of my problem.
document.getElementById("main").onscroll=function(){
console.log("scrolled");
document.getElementById("scrolled").innerHTML="true";
}
#main {
width:100px;
height:100px;
border:1px solid #000;
overflow:hidden;
}
.object {
width:100%;
height:75%;
background:rgba(0,0,0,0.1);
}
<div id="main">
<div class="object"></div>
<div class="object"></div>
<div class="object">Can't see me.</div>
</div>
(The objects inside are expanding, it's just hidden)<br>
Try scrolling (with scroll wheel) in the div, then look below.<br>
Scroll triggered: <span id="scrolled">false</span>
As you can see, no scroll is triggered, that's because there is no scrollbar! Essentially, I would still like to know if the scroll wheel is being used, I'd also like to know if it's being used upwards or downwards.
What's my goal?
Basically, I want to create this "dynamic scrolling" type framework behind my website. So, the container is overflow:hidden, but when the scroll wheel is triggered (either up or down) it increments or decrements a varible which is set as top:(variable) in JavaScript.
So all in all, I just want something that detects if the scroll wheel is being used so I can fulfil my dynamic scrolling script.
Things I'm aware of (so you shouldn't mention them to me)
I'm aware that some people have broken scroll wheels, or don't even have one. So before you mention it. My solution to that is putting buttons at the bottom (or top) of each slide, which scrolls down or up according.
Other
The solution should be pure javascript, that means no libraries! Sorry to put you through the struggle, but that's what I need. The only acception I'll make is if the script is way to long and a library makes it shorter. Otherwise, no no.
That's all!
Thanks for reading through, and good luck find a solution! I'll be furiously trying to find a solution as well. I'll update this post to let you know if I find any information, or require something different.
Feel free to ask questions in the comments.
You can try to set the position gradually in reaction on wheel event. This event is fired every time the mouse wheel is scrolled. Event properties deltaX, deltaY, deltaZ contains the size of shift in given direction.
Snippet http://jsfiddle.net/07516utp/ shows sensing of scroll deltas of this event.
document.addEventListener('wheel', onWheel);
function onWheel(event) {
document.querySelector('#log').innerHTML = 'event: dx=' + event.deltaX + ' dy=' + event.deltaY + ' dz=' + event.deltaZ;
}

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.

Overlapping hover decoration

Given the following images, I'm really not sure how best to approach this issue.
I mean I could make a sprite image and position each link/icon absolute so that when the hover state occurs they don't push each other. However the problem is the clickable area will grow with the hover state thereby overlapping the other buttons and making them hard to click.
Any suggestions/ideas would be greatly appreciated.
Cheers!
My solution thanks to #ioannis-karadimas
http://codepen.io/trev/pen/overlapping-hover-states/2
If there 's no movement involved, there is no reason why you cannot separate the trigger area from the images themselves. Make all triggers invisible divs floating above the graphics, and the clickable area will stay constant and predictable. Changes in the graphics, like overlaying an image with another or changing a sprite's placement need not be related to the clickable area at all.
To help you in the initial positioning and debugging of it, you could initially place a border around each layer, then position them above the graphics. When you are done, remove the border and make the layers transparent filling them with a fully transparent GIF.
Haven't tried but I think it should work.
.button:hover:after {
display: inline;
position: absolute;
left: 60px; // button's width / 2
width: 150px;
height: 150px;
background-image:url('cross.png');
}
You need to create an absolutely positioned elments (or, pseudo-elements) inside the icon containers and show them on mouse over. Thus, it won't affect the hover area.
Here's the example with pseudo-elements, but in case you need support for IE < 8, you can just add normal ones:
http://jsfiddle.net/bFVx8/

Custom cursor interaction point - CSS / JQuery

I'm trying to use a custom cursor for an online game, in this case it's a sniper scope.
The problem is when I reference the cursor via CSS, the interaction point is still the top left of the icon, whereas it needs to be dead center of the icon for the cursor to make any sense.
Here's the cursor:
cursor:url(http://www.seancannon.com/_test/sniper-scope.cur),default;
Here's a demo: http://jsfiddle.net/9kNyF/
If you put the red dot from the cursor over the red dot I created in the demo, it won't fire the click event. You have to attempt to aim the top left corner at it.
If you set the cursor back to cursor:default; you'll see the click event fires just fine, it's just a matter of "aiming" the cursor.
The game is coded in JQuery so if I need to add some logic there for cursor offset or something lame, so be it. Ideally I want this to be a CSS fix.
Thanks!
You just need to provide the hotspot's <x> and <y> position in your CSS:
In your example, the center happens to be 24px in from the top/left (huge ass cursor lol)
cursor:url(http://www.seancannon.com/_test/sniper-scope.cur) 24 24,default;
http://jsfiddle.net/9kNyF/15/ see?
As far as it not firing the click event try placing this around your event listener.
$(function(){
$('#point').click(function(){
alert('clicked point');
});
});
With the centering of the cross hairs it might be easier to use a div with the background of the image and using jQuery to place the div over your cursor in the correct place.
<div id="crosshairs"></div>
<script>
$(function(){
$("body").mousemove(function(e){
var CrossHairWidth = $('#crosshairs').width();
var CrossHairHeight = $('#crosshairs').height();
$('#crosshairs').css('top', e.pageY - (CrossHairHeight / 2));
$('#crosshairs').css('left', e.pageX - (CrossHairWidth / 2) );
});
});
</script>
You then hide the cursor doing something like so:
cursor: url(http://www.seancannon.com/_test/transparent.png), default;
whatever tool you used to create the cursor, should have an option for managing the click target area. You'd be chasing you tail looking for a javascript/css solution.

Categories

Resources