i would like some help with a javascript project.What i'm trying to do ,is have two functions that initiate when mouse is moved,but only one of them works every time
For example,if i do this
var mouseX;
var mouseY;
document.onmousemove = captureMouse;
document.onmousemove = function(){console.log("check");}
function captureMouse(ev){
ev = ev || window.event;
var mousePos= mouseCoords(ev);
mouseX=mousePos.x;
mouseY=mousePos.y;
document.getElementById("coordinput").value=mouseX;
return mousePos;}
function mouseCoords(ev){
if(ev.pageX || ev.pageY){
return {x:ev.pageX,y:ev.pageY};
}
return{
x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
y:ev.clientY + document.body.scrollTop - document.body.clientTop
};
}
If i remove the second document.onmousemove,the first works fine and changes the values of the input field
If i leave it there,it constantly writes check like it should (it's for debug purposes),but the first one doesn't work
Any ideas on how to make multiple mouse events work?
use document.addEventListener('mousemove', yourCb); it allows multiple handlers
Related
Ok. I want to apologize first because I don not even know how to title this question properly.
I will try to explain:
I have a working code for this that kind-of-works, but it is a bit messy, so I am trying to divide it into small functions.
The whole process is a simulated scrollbar:
-div id="scrollbar" represents the scrollbar track.
-div id="scrollbar"'s only child represents the scrollbar thumb.
Now. Whenever an "onmousedown" event takes place in document.documentElement, function checkAndPerform(e) gets executed.
checkAndPerform(e) gets borders of scrollbar and calls the following functions:
-checkClick(e): It checks if click has occurred inside scrollbar and returs true or false.
-perform(): If result of checkClick is true, it moves thumb to click position. Also, it scrolls down the section where the scrollbar operates (section id="ejercicios"). Finally, it adds a "mousemove" EventListener to document.documentElement, with function followMe(e) attached to it.
This followMe function calls a function that limits scroll to trackbar borders. After that it perform translation of div and scrolling of section while "mousemove" is active, and finally calls a function release(), that adds a "mouseup" EventListener to remove the followMe function after mouse button gets released.
The idea for the code was got here:
How can I retrieve all mouse coordinates between mousedown to mouseup event, where you can see in accepted answer that function called "trackPoints" gets called troubleless (as it is in my previous code).
So, here it is troubling javascript code:
function getHeight(object) {
var height = object.getBoundingClientRect().bottom - object.getBoundingClientRect().top;
return height;
}
function checkClick(e) {
console.log("x:" + e.pageX, "y:" + e.pageY);
if (e.pageX > sBLeft - 5 && e.pageX < sBRight + 5 && e.pageY < sBBottom && e.pageY > sBTop) {
adentroBar = true;
console.log("meas adentro");
} else {
adentroBar = false;
console.log("meas afuera");
}
return adentroBar;
}
function scrollLimited(e) {
if (e.pageY < sBTop) {
translateY = 0;
} else if (e.pageY > sBBottom) {
translateY = getHeight(scrollBar) - getHeight(thumb);
} else {
translateY = e.pageY - sBTop - .5 * getHeight(thumb);
}
}
function followMe(e) {
scrollLimited;
thumb.style.transform = "translate3d(0," + translateY + "px,0)";
document.getElementById('ejercicios').scrollTop = translateY * ratio;
document.documentElement.addEventListener("mouseup", release, false);
}
function perform() {
if (adentroBar === true) {
translateY = e.pageY - sBTop - getHeight(thumb) / 2;
}
thumb.style.transform = "translate3d(0," + translateY + "px,0)";
document.getElementById('ejercicios').scrollTop = translateY * ratio;
document.documentElement.addEventListener("mousemove", followMe, false);
}
function release() {
document.documentElement.removeEventListener("mousemove", followMe, false);
}
function checkAndPerform(e) {
var translateY, adentroBar, scrollBar = document.getElementById('scrollbar'),
thumb = scrollBar.getElementsByTagName("div")[0],
sBLeft = scrollBar.getBoundingClientRect().left,
sBRight = scrollBar.getBoundingClientRect().right,
sBTop = scrollBar.getBoundingClientRect().top,
sBBottom = scrollBar.getBoundingClientRect().bottom,
preg = document.getElementById('preguntas'),
ratio = preg.offsetHeight / (getHeight(scrollBar) - getHeight(thumb));
if (e.which === 1) {
checkClick;
perform;
}
}
document.documentElement.addEventListener("mousedown", checkAndPerform, false);
Also, here it is a jFiddle for it: https://jsfiddle.net/pa0exs4q/
I may provide the working code in case you find it interesting, but as i have said, it is really messy and porly written.
Problem is second function in flow (checkClick), is not even being called.
I have tried to call it as (checkClick(e)), in that case it runs, but fails to recognize variables defined above in checkAndPerform.
In my working code, everything was an only function, so i think it may be a scope problem, but I am open for any ideas.
If you have a function named myFunction, you can pass it around simply as myFunction, but to actually call it you have to write it as myFunction(). So this:
function checkAndPerform(e){
var translateY, adentroBar, scrollBar=document.getElementById('scrollbar'), thumb=scrollBar.getElementsByTagName("div")[0], sBLeft=scrollBar.getBoundingClientRect().left, sBRight=scrollBar.getBoundingClientRect().right, sBTop=scrollBar.getBoundingClientRect().top, sBBottom=scrollBar.getBoundingClientRect().bottom, preg=document.getElementById('preguntas'), ratio=preg.offsetHeight/(getHeight(scrollBar)-getHeight(thumb));
if (e.which===1){
checkClick;
perform;
}
}
Should become this:
function checkAndPerform(e){
var translateY, adentroBar, scrollBar=document.getElementById('scrollbar'), thumb=scrollBar.getElementsByTagName("div")[0], sBLeft=scrollBar.getBoundingClientRect().left, sBRight=scrollBar.getBoundingClientRect().right, sBTop=scrollBar.getBoundingClientRect().top, sBBottom=scrollBar.getBoundingClientRect().bottom, preg=document.getElementById('preguntas'), ratio=preg.offsetHeight/(getHeight(scrollBar)-getHeight(thumb));
if (e.which===1){
checkClick(e);
perform(e); // make sure to pass e and expect it in perform, otherwise it won't have access to it
}
}
Without seeing your corresponding markup, this seems to me the most likely and glaring issue in your code.
I found this script by theZillion (http://thezillion.wordpress.com/2012/08/29/javascript-draggable-no-jquery/) that makes a div draggable. I'm trying to use this script to move a div by class name. And not by ID.
I have an event handler that works, but not when I'm adding the script... The console shows no errors either. Any ideas about how to make this work?
This is the code I have:
function wrappmover(){
var moveEvent = "dice-window-wrapper";
var addClassArr= document.getElementsByClassName(moveEvent);
for(var i=0; i<addClassArr.length; i++){
var addClass = addClassArr[i];
addClass.addEventListener("click", movewrapp, true);
}
function movewrapp() {
var classToMove = "dice-window-wrapper";
var elems = document.getElementsByClassName(classToMove);
var tzdragg = function(){
return {
startMoving : function(evt){
evt = evt || window.event;
var posX = evt.clientX,
posY = evt.clientY,
a = document.getElementsByClassName(classToMove),
divTop = a.style.top,
divLeft = a.style.left;
divTop = divTop.replace('px','');
divLeft = divLeft.replace('px','');
var diffX = posX - divLeft,
diffY = posY - divTop;
document.onmousemove = function(evt){
evt = evt || window.event;
var posX = evt.clientX,
posY = evt.clientY,
aX = posX - diffX,
aY = posY - diffY;
tzdragg.move('elem',aX,aY);
}
},
stopMoving : function(){
document.onmousemove = function(){}
},
move : function(divid,xpos,ypos){
var a = document.getElementById(divid);
document.getElementById(divid).style.left = xpos + 'px';
document.getElementById(divid).style.top = ypos + 'px';
}
}
}();
Okay, so you want to have draggable elements on your page?
Take a look at the following code (and here's a working example). I hope you will find it self-explanatory, but just in case there are also comments:
// Wrap the module in a self-executing anonymous function
// to avoid leaking variables into global scope:
(function (document) {
// Enable ECMAScript 5 strict mode within this function:
'use strict';
// Obtain a node list of all elements that have class="draggable":
var draggable = document.getElementsByClassName('draggable'),
draggableCount = draggable.length, // cache the length
i; // iterator placeholder
// This function initializes the drag of an element where an
// event ("mousedown") has occurred:
function startDrag(evt) {
// The element's position is based on its top left corner,
// but the mouse coordinates are inside of it, so we need
// to calculate the positioning difference:
var diffX = evt.clientX - this.offsetLeft,
diffY = evt.clientY - this.offsetTop,
that = this; // "this" refers to the current element,
// let's keep it in cache for later use.
// moveAlong places the current element (referenced by "that")
// according to the current cursor position:
function moveAlong(evt) {
that.style.left = (evt.clientX - diffX) + 'px';
that.style.top = (evt.clientY - diffY) + 'px';
}
// stopDrag removes event listeners from the element,
// thus stopping the drag:
function stopDrag() {
document.removeEventListener('mousemove', moveAlong);
document.removeEventListener('mouseup', stopDrag);
}
document.addEventListener('mouseup', stopDrag);
document.addEventListener('mousemove', moveAlong);
}
// Now that all the variables and functions are created,
// we can go on and make the elements draggable by assigning
// a "startDrag" function to a "mousedown" event that occurs
// on those elements:
for (i = 0; i < draggableCount; i += 1) {
draggable[i].addEventListener('mousedown', startDrag);
}
}(document));
Load or wrap it in <script></script> tags as close as possible to </body> so that it doesn't block the browser from fetching other resources.
Actually, if you remove the comments, it's a very small function. Much smaller and more efficient than the one from the website you've provided.
A possible improvement
Consider replacing the anonymous wrapper with something like makeDraggable(selector); where selector is a CSS selector, so you could do crazy stuff like:
makeDraggable('#dragMe, #dragMeToo, .draggable, li:nth-child(2n+1)');
It can be achieved by using document.querySelectorAll that is able to perform complex CSS queries instead of a simple class name lookup by document.getElementsByClassName.
Things to watch out for
If the page has any scrolling - the drag will look broken; consider adjusting the positioning of the dragged element by scrollX and scrollY
This will obviously not work in Internet Explorer (figure it out yourself).
There might be memory leaks (needs profiling and testing).
EDIT: A solution for adding new draggable elements
So you want to be able to add more draggable elements? There are several approaches to tackle this. For example you could write a makeDraggable(element); function and call it on the element you're adding to the DOM. It will work of course, but let's have a look at something different, shall we?
Instead of querying the DOM in search of draggable elements and assigning them event listeners, why don't we assign just one for the "mousedown" event on document body.
When triggered, the event object will contain a reference to the target element which is the object the event has been dispatched on (the element you mousedown-ed). The relevant part of the code will now resemble this:
// Performs a check if the current element is draggable and if yes,
// then the dragging is initiated:
function startDragIfDraggable(evt) {
// Check if the target element (referenced by evt.target) contains a
// class named "draggable" (http://stackoverflow.com/questions/5898656/):
if (evt.target.classList.contains('draggable')) {
// Invoke startDrag by passing it the target element as "this":
startDrag.call(evt.target, evt);
}
}
// Listen for any "mousedown" event on the document.body and attempt dragging
// the target element (the one where "mousedown" occurred) if it's draggable:
document.body.addEventListener('mousedown', startDragIfDraggable);
And here's a working example of the above code. As a bonus it features a simulation of adding new draggable elements to the DOM.
In addition to being able to drag dynamically-added draggable elements, this approach will also save us some memory because we can now avoid assigning a bunch event listeners to a bunch of elements. However, if the application you're developing is very click-intensive (e.g. a game) then you might waste a little bit of CPU because of checks on every click.
I am writing a plugin that creates a select marquee box when the mouse is clicked on a canvas div. So far i've written the plugin with the click() and mousemove() events adjacent to each other in the code (i dont know of any way to embed events inside other events). But this becomes a problem when the mouse moves over the canvas before a click. Does anyone know how to initialize a mousemove handler with a click?
Here is the code i have written so far:
$.fn.createBox = function(id) {
$(id).css({
cursor:'crosshair'
});
$(id).click(function(e) {
var clickLocX = e.pageX;
var clickLocY = e.pageY;
$('<div>').attr({
'class':'newBox',
'ctr':'on'
})
.css({
top:clickLocY,
left:clickLocX
})
.appendTo(id);
});
//Mousemove must be initialized only AFTER the click. HOW TO DO THIS?
$(id).mousemove(function(e){
var XpageCoord = e.pageX;
var YpageCoord = e.pageY;
window.Xloc = XpageCoord;
window.Yloc = YpageCoord;
var boxOffset = $('.newBox').offset();
var boxHeight = YpageCoord - boxOffset.top;
var boxWidth = XpageCoord - boxOffset.left;
$('.newBox').css({
height:boxHeight + 'px',
width:boxWidth + 'px'
});
});
}
Just set the mousemove handler from within the click handler and use a flag to determine whether or not it has already been set to avoid adding it multiple times.
EDIT: An example
assignedMoveHandler = false;
$(id).click(function(e) {
// other stuff...
if(!assignedMoveHandler) {
$(id).mousemove(function(e) {
// mouse move handling code...
});
assignedMoveHandler = true;
}
}
I use a global here which you may or may not care about, I lookup the jquery object twice, etc., so it could be cleaned up a bit, but this is the general idea. When the click handler fires you check to see if you've assigned a mousemove handler yet. If not, assign it.
iOS 5 has brought a number of nice things to JavaScript/Web Apps. One of them is improved scrolling. If you add
-webkit-overflow-scroll:touch;
to the style of a textarea element, scrolling will work nicely with one finger.
But there's a problem. To prevent the entire screen from scrolling, it is recommended that web apps add this line of code:
document.ontouchmove = function(e) {e.preventDefault()};
This, however, disables the new scrolling.
Does anyone have a nice way to allow the new scrolling within a textarea, but not allow the whole form to scroll?
Update Per Alvaro's comment, this solution may no longer work as of iOS 11.3.
You should be able to allow scrolling by selecting whether or not preventDefault is called. E.g.,
document.ontouchmove = function(e) {
var target = e.currentTarget;
while(target) {
if(checkIfElementShouldScroll(target))
return;
target = target.parentNode;
}
e.preventDefault();
};
Alternatively, this may work by preventing the event from reaching the document level.
elementYouWantToScroll.ontouchmove = function(e) {
e.stopPropagation();
};
Edit For anyone reading later, the alternate answer does work and is way easier.
The only issue with Brian Nickel's answer is that (as user1012566 mentioned) stopPropagation doesn't prevent bubbling when you hit your scrollable's boundaries. You can prevent this with the following:
elem.addEventListener('touchstart', function(event){
this.allowUp = (this.scrollTop > 0);
this.allowDown = (this.scrollTop < this.scrollHeight - this.clientHeight);
this.prevTop = null;
this.prevBot = null;
this.lastY = event.pageY;
});
elem.addEventListener('touchmove', function(event){
var up = (event.pageY > this.lastY),
down = !up;
this.lastY = event.pageY;
if ((up && this.allowUp) || (down && this.allowDown))
event.stopPropagation();
else
event.preventDefault();
});
For anyone trying to acheive this with PhoneGap, you can disable the elastic scrolling in the cordova.plist, set the value for UIWebViewBounce to NO. I hope that helps anyone spending ages on this (like i was).
ScrollFix seems to be perfect solution. I tested it and it works like a charm!
https://github.com/joelambert/ScrollFix
/**
* ScrollFix v0.1
* http://www.joelambert.co.uk
*
* Copyright 2011, Joe Lambert.
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
var ScrollFix = function(elem) {
// Variables to track inputs
var startY, startTopScroll;
elem = elem || document.querySelector(elem);
// If there is no element, then do nothing
if(!elem)
return;
// Handle the start of interactions
elem.addEventListener('touchstart', function(event){
startY = event.touches[0].pageY;
startTopScroll = elem.scrollTop;
if(startTopScroll <= 0)
elem.scrollTop = 1;
if(startTopScroll + elem.offsetHeight >= elem.scrollHeight)
elem.scrollTop = elem.scrollHeight - elem.offsetHeight - 1;
}, false);
};
It was frustrating to discover a known problem with stopPropagation and native div scrolling. It does not seem to prevent the onTouchMove from bubbling up, so that when scrolling beyond the bounds of the div (upwards at the top or downwards at the bottom), the entire page will bounce.
More discussion here and here.
I'm trying to drag and drop items on a page, but insteads of moving the item itself, I create a copy of it.
Here is my code. "copyDragDrop" is a div at the bottom of the page. It remains empty until the user strats dragging something.
function coordSouris()
{
return {
x:event.clientX + document.body.scrollLeft - document.body.clientLeft,
y:event.clientY + document.body.scrollTop - document.body.clientTop
};
}
function drag()
{
var pos = coordSouris(event);
copie = event.srcElement.cloneNode(true);
document.getElementById('copieDragDrop').appendChild(copie);
copie.style.position = 'absolute';
copie.style.display = 'block';
document.onmousemove = mouseMove;
document.onmouseup = drop;
}
function mouseMove()
{
if (copie != null)
{
var pos = coordSouris(event);
copie.style.left = pos.x;
copie.style.top = pos.y;
}
}
function drop()
{
var divCopie = document.getElementById('copieDragDrop');
if (divCopie.hasChildNodes() )
{
while ( divCopie.childNodes.length >= 1 )
{
divCopie.removeChild(divCopie.firstChild);
}
}
}
This code creates the copy, starts to move it, but after a few pixels, the copy stops following the mouse. If I release the mouse, "onmouseup" is not fired, but the copy starts to follow the mouse again ! I've tried the code on several items, the same bug occurs
I don't understand anything, any help is more than welcome.
UPDATE : I juste realised that all elements I tried the code on had something in common : they contained or were included in an ASP.net hyperlink control. The same code works well on regular HTML elements. There must be some auto-generated javascript for links that interferes with my code.
Couldn't find the auto-generated code responsible for the issue, so I simply solvec this by replacing Hyperlink controls by standard HTML anchors.