Get position relative to parent not viewport on mousedown / touchstart - javascript

How, on mousedown or touchstart, can I get the position of the mouse relative to or within the container or element it took place in.
So, I know I can get the pageX/Y or clientX/Y from the event or on touch, the original event. But that is the positioning compared to the whole viewport. This div is absolute positioned, so how can I get it positioned within the boundaries of its container ( absolute positioned ).
So this works: http://jsfiddle.net/Fb6An/
Here is the code:
$('.dataCard').on('mousedown touchstart', function(event){
$(this).children('.ripple').show().css({'left':event.clientX || event.originalEvent.clientX, 'top':event.clientY || event.originalEvent.clientY});
});
Except it doesn't work in some elements on the full site that I am working on. On elements that are closer to the right edge of the screen, it appears more to the right and lower than the the actual mouse location. In elements to the left, it works fine.
So I was wondering how can I get the position within an element and style the .ripple there?

No need for jQuery when finding the coordinates to set as the style:
$('.dataCard').on('mousedown touchstart', function(event){
var clickX = (event.clientX || event.originalEvent.clientX),
clickY = (event.clientY || event.originalEvent.clientY);
$(this).children('.ripple').each(function(){
$(this)
.show()
.css({
left: clickX - this.offsetParent.offsetLeft,
top: clickY - this.offsetParent.offsetTop
});
});
});
Updated fiddle from #Gaby aka G. Petrioli: http://jsfiddle.net/s52u4/1/
In this particular case, using jQuery is like hitting a thumbtack with a sledge hammer.

You could use position() on your container and calculate that in combination with event.screenX, event.screenY. That's what i would do in this case. Might as well wrap it into an extension

You need to find the .offsetParent() (closest element that is positioned) and subtract its .offset() values..
$('.dataCard').on('mousedown touchstart', function(event){
var clickX = (event.clientX || event.originalEvent.clientX),
clickY = (event.clientY || event.originalEvent.clientY);
$(this).children('.ripple').each(function(){
var ripple = $(this).show(),
container = ripple.offsetParent(),
offset = container.offset(),
correctX = clickX - offset.left,
correctY = clickY - offset.top;
ripple.css({ left: correctX, top: correctY});
});
});
Demo at http://jsfiddle.net/s52u4/

Related

mousemove based scrolling script need to adapt for touchscreen use

I have a Javascript that scrolls an UL from left to right depending on where the mouse is positioned over it: A demo can be seen HERE (site still under construction) I would like it to work with touchscreen devices also. Whereby touching and "dragging" ones finger would scroll the UL in a similar manner, tapping on the list would then "click" on an image.
How easy/hard would that be to modify the JS:
$(function(){
$(window).load(function(){
var $gal = $("#gallerylist.top"),
galW = $gal.outerWidth(true),
galSW = $gal[0].scrollWidth,
wDiff = (galSW/galW)-1, /// widths difference ratio
mPadd = 200, // Mousemove Padding
damp = 20, // Mousemove response softness
mX = 0, // Real mouse position
mX2 = 0, // Modified mouse position
posX = 0,
mmAA = galW-(mPadd*2), // The mousemove available area
mmAAr = galW/mmAA; /// get available mousemove fidderence ratio
$gal.mousemove(function(e) {
mX = e.pageX - $(this).parent().offset().left - this.offsetLeft;
mX2 = Math.min( Math.max(0, mX-mPadd), mmAA ) * mmAAr;
});
setInterval(function(){
posX += (mX2 - posX) / damp; /// zenos paradox equation "catching delay"
$gal.scrollLeft(posX*wDiff);
}, 10);
});
});
There are touch events similar to mouse events:
$gal.touchstart(function(e) {});
$gal.touchend(function(e) {});
$gal.touchmove(function(e) {});
Usually you don't need to implement touch events for 'click'... they are usually already in sync.
For the mousemove event, it may be more desirable from the user's perspective to have this functionality disabled on a touch device. If the page needs to scroll, this could interfere with that interaction. An alternative may be to have some animation on a window scroll event.

mouseMove very laggy

I am trying to obtain the image effect that 99designs is obtaining when hovering a mouse over a design.. [99designs.ca] Logo design contest: Runningbug Needs Logo 220626
I am currently obtaining the position of the mouse on mousemove, then using that to move my popover <img>, and everything works fine, but it is very laggy.. and presumably its from so many calls being made.
To get the position of the mouse:
jQuery(document).ready(function(){
//$("#special").click(function(e){
$(".imgWrap").mousemove(function(e){
//$('#status2').html(e.pageX +', '+ e.pageY);
//alert(e.pageX + ', ' + e.pageY);
mouseX = e.pageX;
mouseY = e.pageY;
});
})
I'm not sure of another way I can do this.. any ideas?
On the full course of events is the following:
User mouses over an img tag.
I get the position of the mouse as per above.
The <img> tag also calls a js function which changes the position of an img tag to the position of the mouse.
Actually, you can check it here: pokemonsite
update: I see there is a bounty placed (thanks !). I'm a little busy at the moment and can't check all the other answers, but I'll make sure to check them asap
There are several ways to improve performance when using mousemove events.
Use backface-visibility: hidden on popover element to force hardware acceleration. Same thing can be achived with transform: translate3d(0,0,0) but that makes difficult to use CSS transform function (see point #2).
Use CSS transform function for absolute positioning to avoid repaints but keep popover element absolute or fixed positioned.
When setting inline CSS via JS use requestAnimationFrame to avoid unnecesary layout trashing.
(maybe, optionally) hide cursor when hovering and use popover element as position indicator.
Move everything you can from JS to CSS ie. :hover state can be used to toggle display of popover element.
I made demo example combining all things listed. There is still some latency between cursor position and popover image and none of example links in original question work so I can't compare against it but I hope someone finds this useful.
DEMO
<div id="imgHolder" class="imgHolder">
<img src="//placehold.it/200x200" alt="" />
</div>
<div id="bigImgHolder" class="imgHover">
<img src="//placehold.it/500x500" alt="" />
</div>
.imgHover {
display: none;
backface-visibility: hidden;
position: fixed;
top: 0; left: 0;
pointer-events: none;
}
.imgHolder:hover ~ .imgHover { display: block; }
// uncomment if it makes sense
//.imgHolder:hover { cursor: none; }
var posX, posY;
$('#imgHolder').mousemove(HoverImg);
function HoverImg(e) {
posX = e.pageX;
posY = e.pageY;
window.requestAnimationFrame(showBigImg);
}
function showBigImg() {
$('#bigImgHolder').css({'-webkit-transform': 'translateX(' + posX + 'px) translateY(' + posY + 'px)', 'transform': 'translateX(' + posX + 'px) translateY(' + posY + 'px)' });
}
references:
http://davidwalsh.name/translate3d
http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
https://css-tricks.com/using-requestanimationframe/
Try this:
jQuery(document).ready(function(){
$(".imgWrap").mousemove(function(e){
e.stopPropagation();
mouseX = e.pageX;
mouseY = e.pageY;
});
})
Use e.offsetX and e.offsetY or (recommended) e.clientX and e.clientY instead of pageX and pageY. Maybe this will be a better solution. Note: offsetx and offsety do not work in Firefox as far as I know.
If the absolute (x, y) position is not so important (meaning: some pixel-values can be omitted without destroying your logic), you could try to skip some frames of your mousemove-event.
var globalSkipCounter = 0;
var globalSkipRate = 5;
$(".imgWrap").mousemove(function(e){
if(globalSkipCounter >= globalSkipRate){
var mouseX = e.pageX;
var mouseY = e.pageY;
do_stuff(mouseX, mouseY);
globalSkipCounter = 0;
}
else{
globalSkipCounter+=1;
}
});
This way, you omit redrawing your image on every mousemove-event, instead your draw-routines (do_stuff) are only invoked, once every 5 events.
Cache the position and wrap the update in an if(oldpos !== newpos) type check (remembering to update oldpos inside it).
Use requestAnimationFrame to handle the update - if you have a normal function and pass that as the callback then it will only get called once per frame (ie, don't use an anonymous function).
Finally make use of transform:translate(x,y) to set the position and make better use of the GPU etc. Related to this, there's no harm in making use of the css will-change keyword if you want to use top/left instead.
try a maximum event per second based approach:
jQuery(document).ready(function(){
var now, then, delta, interval = 1000/60; //maximum 60 eps, you can change
//$("#special").click(function(e){
$(".imgWrap").mousemove(function(e){
now = Date.now();
delta = now - then;
if (delta > interval) {
then = now - delta % interval; //subtract extra waited time
//$('#status2').html(e.pageX +', '+ e.pageY);
//alert(e.pageX + ', ' + e.pageY);
mouseX = e.pageX;
mouseY = e.pageY;
// do your thing
}
});
})
EDIT: didn't know then is a reserved word in javascript, you can rename it.
I have a sortable listview in angular, but dragging items with the mouse in chrome was lagging a lot. I tried disabling all chrome extensions and now the lag is totally gone.
It turned out that TamperMonkey was causing it.
Instead of using "left" or "right" property of CSS rather try to use
"transform" I was trying to do the same thing as asked in question, and using "transform" worked for me like a charm.
for example:
transform: translate(30px);
this will move the element, 30px to the right.
by using transform we can move elements to left, right, top and bottom according to the need.

How can I make a DOM element's height resizable?

I have a widget in my ui that resides in a fixed location on the right bottom corner of my browser.
I want the user to be able to click on the header portion of the widget and be able to drag it upwards and effectively increase the height of the widget. The widgets bottom, left, and right properties would be unchanged but top should be able to change to allow up to the max height of the widget as defined by it's css max-height.
Are there any examples of something like this out there? I know jQueryUI has the resizable behavior but unfortunately I cannot use jQueryUI on this project. We are however using jQuery.
Any tips or ideas or jsfiddle exaples are greatly appreciated. Just something to get me going in the right direction. I looked a CSS3 resizable and it puts that standard resizing icon in the right bottom corner, like this textarea.
Perhaps this plugin can help?
http://dev.iceburg.net/jquery/jqDnR/
It is possible to do this with just jQuery. Off the top of my head, you could probably do something like this:
<div id="widget">
<h3 id="widget-header">Header</h3>
some content
</div>
<script language="javascript" type="text/javascript">
var clientY = 0;
var offset = null;
var changeSize = false;
$(function () {
$("#widget-header")
.mousedown(function (event) {
clientY = event.pageY;
offset = $("#widget").offset();
changeSize = true;
})
.mousemove(function (event) {
if (changeSize) {
// get the changes
var difY = event.pageY - clientY;
offset.top += difY;
// animate the changes
$("#widget").offset({ top: offset.top, left: offset.left });
$("#widget").height($("#widget").height() - difY);
// update the new positions
clientY = event.pageY;
}
})
.mouseup(function (event) { changeSize = false; })
.mouseout(function(event) { changeSize = false; });
});
</script>

How to drag a DIV so its width and height are negative values?

I'm a bit stumped here. I am developing a feedback utility that will allow the user to "draw" boxes on a web page to highlight problem areas. Right now I have an overlay DIV that fills the screen and jQuery allows you to draw red outlined DIVs by clicking and dragging.
Here is the JS:
{
var $feedbackOverlay = jQuery('#feedbackOverlay');
var $original = { top: 0, left:0 };
$feedbackOverlay.bind('mousedown', function (e)
{
jQuery('<div id="currentHighlight"></div>')
.css('width', '1px')
.css('height', '1px')
.css('border', 'solid 3px #ff0000')
.css('border-radius', '5px')
.css('position', 'absolute')
.css('left', e.pageX)
.css('top', e.pageY)
.css('z-index', '8000001')
.appendTo('body');
$original = { top: e.pageY, left: e.pageX };
});
$feedbackOverlay.bind('mousemove', function (e)
{
var $currentHighlight = jQuery('#currentHighlight');
if ($currentHighlight.length > 0)
{
var $pos = { top: e.pageY, left: e.pageX };
if($pos.top < $original.top) $currentHighlight.css('top', $pos.top);
if ($pos.left < $original.left) $currentHighlight.css('left', $pos.left);
$currentHighlight.height(Math.abs($pos.top - $original.top));
$currentHighlight.width(Math.abs($pos.left - $original.left));
}
});
$feedbackOverlay.bind('mouseup', function (e)
{
var $currentHighlight = jQuery('#currentHighlight');
$currentHighlight.removeAttr('id');
});
var $feedbackInstructions = jQuery('#feedbackInstructions');
$feedbackInstructions.fadeIn(1000, function ()
{
setTimeout(function ()
{
$feedbackInstructions.fadeOut(1000);
}, 3000);
});
$feedbackOverlay.height(jQuery(document).height());
});
Here is a jsFiddle for the above:
http://jsfiddle.net/Chevex/RSYTq/
The problem is that I can't drag the boxes up or left. The first click puts the top left corner where the mouse clicked. After that subsequent dragging will change the width of the box. Letting go of the mouse completes the box and you may then start drawing another one. If you try to drag the DIV left or up while drawing it's width will remain at 0 but won't go negative.
Here you can find working solution: http://jsfiddle.net/RSYTq/34/
Something like this will get you closer to what you want: http://jsfiddle.net/RSYTq/18/
Doesn't quite handle move up and to the left and then switching to moving down and to the right quite right yet but it gives you the idea.
There's no such thing a a negative width - these are not coorindinates. You need to reposition and recalculate the corner positions relative to the corner that's not being moved.
Sounds like you need to check if the click origin (x,y) is > than the current mouse position, and then swap which one you use for the CSS top-left.
You would need to track the original start point somewhere (variables, data attributes on #currentHighlight, wherever you want), and check for width or height < 0. When so, set the #currentHighlight left/top CSS to be offset by original + (e.pageX - $currentHighlight.position().left) (for example). Then set the #currentHighlight width/height to the same difference (but positive: (e.pageX - $currentHighlight.position().left) * -1).

How do I get the absolute position of a mouse click from an onClick event on the body?

I am trying to get the absolute position (top and left) of a mouse click relative to the browser/body, not any parent elements within the body.
I have a listener bound to the body, but e.pageX and e.pageY are giving me the position relative to a div.
Note that I can leverage jQuery and YUI functions.
Code that currently does not work correctly:
//getting the position
function _handleClick(e) {
var data = { absX: e.pageX, absY: e.pageY};
_logClickData(data);
}
//binding the function
var methods = {
init: function () {
$("body").click(_handleClick);
}
};
The commenter is correct. pageX and pageY give you the mouse position relative to the entire document not its parent div. But if you're interested you can get the position relative to the document from the position relative to a div.
Get the position of the parent div relative to the body, then add the two values.
x = parentdiv.style.left + e.pageX;
y = parentdiv.style.top + e.pageY;
(0,0)
_____________________
|
|
| __________
|----100----| |
| |---60---* |
| |__________|
|
|
* = Mouse Pointer
I made the diagram because it was fun. Not because I felt you needed one!!
Also, for the above to work you may need to parseInt.
According to this (http://docs.jquery.com/Tutorials:Mouse_Position), those should give you absolute positions. offsetX/Y gives you the relative position.
Edit November 2013: the original "Mouse Position" link seems to be broken, but the documentation for pageX contains an example that utilizes jQuery pageX/Y. The page about the offset method also contains relevant examples.
If I understood your question well this would be the solution
$("body").click(function(e){
var parentOffset = $(this).offset();
var relX = e.pageX - parentOffset.left;
var relY = e.pageY - parentOffset.top;
window.alert(relX);
window.alert(relY);
});
I guess you can use window.pageXOffset, window.pageYOffset property
document.body.addEventListener('click',(e)=>{
console.log(e.clientX + window.pageXOffset, event.clientY + window.pageYOffset)
}
)
The solution, hinted at by #Mrchief's link of http://docs.jquery.com/Tutorials:Mouse_Position, was to bind to the document and not the body element.
//binding the function
var methods = {
init: function () {
$(document).click(_handleClick);
}
};

Categories