Javascript dragging object - javascript

I need to make one div draggable and sets it's left position in according to mouse position.
I've searched a bit and this is what i have so far:
container = $('<div></div>').appendTo($('body')).addClass('container');
someText = $('<div>Some text</div>').appendTo(container);
slider = $('<div></div>').appendTo(container);
slider.addClass('slider');
var isDragging = false;
slider.on('mousedown', function () {
isDragging = true;
});
$(window).on('mouseup', function () {
console.log('mouseup');
isDragging = false;
});
container.on('mouseleave', function () {
console.log('mouseleave');
isDragging = false;
});
container.on('mousemove', function (e) {
if (isDragging) {
var newLeft = parseInt((e.pageX - container.offset().left), 10);
console.log(newLeft);
slider.css('left', newLeft);
}
});
http://jsfiddle.net/w9gxxuvw/2/
The white box is one which should be draggable, but there are a few drawbacks.
First of all, while i drag with my LPM down i select upper text.
Secondly, on chrome when i drag it fast, it doesn't fire mouse up event, so 'slider' just follows cursor while it's moving inside 'container' and i need to click somewhere to stop.
It's not necessary for me to use jQuery, but i don't won't to use another big framework nor jquery plugins.

You can prevent text selection with the user-select CSS property:
container = $('<div></div>').appendTo($('body')).addClass('container');
someText = $('<div>Some text</div>').appendTo(container);
slider = $('<div></div>').appendTo(container);
slider.addClass('slider');
var isDragging = false;
slider.on('mousedown', function () {
isDragging = true;
});
$(window).on('mouseup', function () {
isDragging = false;
});
container.on('mouseleave', function () {
isDragging = false;
});
container.on('mousemove', function (e) {
if (isDragging) {
var newLeft = parseInt((e.pageX - container.offset().left), 10);
slider.css('left', newLeft);
}
});
.container {
display:block;
width:400px;
height:100px;
background: red;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
}
.slider {
display:block;
width:10px;
height:10px;
background: #fff;
position:relative;
left: 0%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I cannot reproduce the problem of the draggable sticking to the mouse in Chrome 42, Firefox 36 or Safari 7. Above example runs flawlessly for me.

Preventing the default action for text selection in the script seems more logical, it has deeper support than CSS user-select as well. Since (most of) the events are connected in this function, I'd nest them. It'll allow for a bit of optimisation. It also makes sense to unbind the mousemove, after several events you may generally start to notice sluggish behaviour otherwise.
...
container.on('mousedown', function(e) {
e.preventDefault();
});
slider.on('mousedown', function() {
$(window).one('mouseup', function() {
console.log('mouseup');
container.off('mousemove');
});
container.on('mousemove', function(e) {
var newLeft = Math.round(e.pageX-container.offset().left);
console.log(newLeft);
slider.css('left', newLeft);
})
.one('mouseleave', function() {
console.log('mouseleave');
container.off('mousemove');
});
});
http://jsfiddle.net/w9gxxuvw/8/

Related

how to fire window.resize only once?

Here is my code;
$(window).resize(function() {
if ($(window).width() >= 760) {
$('.sidebar-left').one().css("display", "block")
} else if ($(window).width() <= 760) {
$('.sidebar-left').one().css("display", "none")
}
});
var m = $(".menu");
m.addClass('fa-bars');
m.on('click', function() {
if (m.hasClass('fa-bars')) {
m
.removeClass('fa-bars')
.addClass('fa-times');
$(".sidebar-left").css("display", "block")
} else {
m
.removeClass('fa-times')
.addClass('fa-bars');
$(".sidebar-left").css("display", "none")
}
});
.sidebar-left {
width: 100px;
position: fixed;
background-color: black;
height: 100%;
}
.menu.fa {
display: none;
}
#media handheld,
screen and (max-width: 760px) {
.sidebar-left {
display: none;
width: 100%;
background-color: black;
height: 100px;
}
.menu.fa {
display: inline-block;
font-size: 35px;
color: black;
}
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.2/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="menu fa"></div>
<div class="sidebar-left"></div>
And a plunk. (may be easier to test here)
So it's just a simple menu. The issue I have is that currently the resize event will run every time the page is resized, so if I have the menu displayed under 760 pixels it will automatically disappear on resize if I have it opened.
If I take the javascript resize function out completely and leave it to the css, the problem is if I open and close the menu in a smaller window, the display will be set to none when I make the window larger again.
I've tried a few other things, but can't seem to think of a way of doing this. The aim is to just to replicate what the $(window).resize function is doing currently, but for that event to fire only once the window has resized past the points.
Hope the problem is explained clearly.
Thanks
You could use jQuery's one() method, which gets activated exactly once:
$(window).one('resize', function() {
if ($(window).width() >= 760) {
$('.sidebar-left').one().css("display", "block")
} else if ($(window).width() <= 760) {
$('.sidebar-left').one().css("display", "none")
}
});
This will solve your question as asked, but i would actually recommend not disabling the resize event and just handling it better, i.e. checking whether it is proper to open/close the sidebar.
Here's a plugin I made, with a resize begin handler, a resize end handler and of course an on resize handler.
(function() {
var resizeEventHandler = function(resizeTarget, _maxResizeWaitTime, onResizeBeginCB, onResizeEndCB, onResizeCB) {
if(resizeTarget){
var resizeTimer = null;
var resizeBegun = false;
var maxResizeWaitTime = _maxResizeWaitTime || 500;
resizeTarget.addEventListener('resize', function() {
if (!resizeBegun) {
if(onResizeBeginCB){
onResizeBeginCB();
}
resizeBegun = true;
}
if(onResizeCB){
onResizeCB();
}
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
if(onResizeEndCB){
onResizeEndCB();
}
resizeBegun = false;
}, maxResizeWaitTime);
});
}else{
console.error('No resizeTarget specified!');
}
};
this.resizeEventHandler = resizeEventHandler;
}).call(this);
Call it like this:
function onResizeBegin() {
$("body").append('onResizeBegin<br/>')
}
function onResizeEnd() {
$("body").append('onResizeEnd<br/>')
}
function onResize() {
$("body").append('onResize<br/>')
}
var resizeEventHandler1 = new resizeEventHandler(window, 500, onResizeBegin, onResizeEnd, onResize);
JSfiddle: https://jsfiddle.net/sv52m1y2/3/

Avoid mouseover mouseleave conflicts

I'm working with this js fiddle here: http://jsfiddle.net/tPx6x/
The animation works like so
You hover over the text, a circle fades in & begins to pulse 1 second later for as long as your mouse is over the text.
When your mouse pointer leaves the text, the pulse stops after one second and the circle fades out.
The issue arises when you do this:
Put your mouse over the text, remove the pointer from the text, THEN place the pointer back over the text before the script has a chance to finish(1-1.4s).
You won't be able to make the circle appear properly agin...you will have to allow the script to reset. That is the problem.
What is the best way to tackle this issue?
Example code:
<div class='circle__title_project-management'>
<h1>project management</h1>
</div>
<div class='circle__project-management hidden'></div>
.circle__project-management, .circle__title_project-management
{
display: inline-block;
cursor: pointer;
}
.circle__project-management
{
margin-left: 8px;
vertical-align: -4.07px;
background-color: transparent;
border: 2px solid #00DBFF;
width: 30px;
height: 30px;
border-radius: 90px;
top: 280px;
left: 40px;
}
.hidden
{
visibility: hidden;
}
.visible
{
visibility: visible;
}
.animate-infinite
{
animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
}
var circleTitle = $('.circle__title_project-management h1');
var circle = $('.circle__project-management');
var initTimeout = 1000;
var initTimeoutPlus = 1400;
circleTitle.mouseover( function() {
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
});
circleTitle.mouseleave( function() {
setTimeout( function() {
circle.stop().removeClass('pulse animate-infinite visibility');
circle.addClass('fadeOut');
}, initTimeout);
setTimeout( function() {
circle.removeClass('fadeOut');
circle.addClass('hidden');
}, 1400);
});
You should note that setTimeout has a return value. You want to clear previous timeouts before you start new ones; otherwise you can get a timeout queue which completely skews your animations. Something like this:
var myTimeout;
...
clearTimeout(myTimeout);
myTimeout = setTimeout(...);
Not sure if this is exactly what you were going for, but along these lines: http://jsfiddle.net/FYY38/
More info here: http://www.w3schools.com/js/js_timing.asp
Also, it looks like the circle.stop() call is doing nothing (as it's css-animated)
To avoid antagonist behaviours, maybe add a class to your element to tag it when the event is triggered and remove it when another is triggered.
That way you can stay in control of what's going on.
you can set time out to mouse over function to cover the time delay for mouseleave.
note that the first run must be without delay
var initTimeout = 1000;
var initTimeoutPlus = 1400;
var firstrun = true;
circleTitle.mouseover( function() {
if (firstrun) {
initTimeoutPlus = 0;
firstrun = false;
} else initTimeoutPlus = 1400;
setTimeout(function() {
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
}, initTimeoutPlus);
});
Probably if you just add a key on mouseover, and toggle it after mouseleave, and before you trigger any mouseleave timeout events, check the key, if it is set, ignore, else go ahead and execute mouseleave
this way if the key is "on" it means a mouse over occurred, if it was off, it means the mouseleave occurred and it is still occurring
var key = false;
circleTitle.mouseover( function() {
key = true;
circle.removeClass('hidden');
circle.addClass('animated fadeIn');
setTimeout( function() {
circle.addClass('pulse animate-infinite');
circle.removeClass('fadeIn');
}, initTimeout);
});
circleTitle.mouseleave( function() {
key = false;
setTimeout( function() {
if (!key){
circle.stop().removeClass('pulse animate-infinite visibility');
circle.addClass('fadeOut');
}
}, initTimeout);
setTimeout( function() {
if (!key){
circle.removeClass('fadeOut');
circle.addClass('hidden');
}
}, 1400);
});

Disable scrolling, but maintain ability to zoom

On a responsive site I'm developing I have my own little lightbox-script which opens images fullscreen while maintaining their aspect ratio. It's pretty simple, uses 2 divs (outer fullscreen-div with black background "lbBlack" and inner div with image "lbImg"):
//super small lightbox ;)
$("#posts").on("click", ".img", function(event) {
$('#lbBlack').css('top',$(document).scrollTop());
$('#lbImg').attr('src', $(this).attr('src'));
$('#lbBlack').css('width',$(window).width());
$('#lbBlack').css('height',window.innerHeight);
$('#lbBlack').fadeIn(500);
$('#lbImg').css('margin-top',((window.innerHeight-$('#lbImg').height()))/2);
document.body.style.overflow="hidden";
document.ontouchmove = function(event){
event.preventDefault();
}
$('#lbBlack').on("click", "#lbImg, body", function(event) {
$('#lbBlack').fadeOut(500);
document.body.style.overflow="visible";
document.ontouchmove = function(event){
return true;
}
});
});
For iOS, I had to add the ontouchmove-prevention, because body-overflow-hidden wasn't enough to avoid scrolling while the lightbox is opened.
Now the "big problem" for this working solution above: I want to enable zooming on the image. This is prevented with the "ontouchmove"-code.
Any ideas?
HTML-code:
<body>
<div id="lbBlack">
<img id="lbImg">
</div>.....
CSS-code:
#lbBlack {
display: none;
position: absolute;
left: 0;
background-color: black;
z-index: 2001;
text-align: center;
}
#lbBlack #lbImg {
max-height: 100%;
max-width: 100%;
position: relative;
}
So I think what I am looking for is a method to prevent scrolling while still maintaining the possibility to zoom. I still don't get it why body-overflow:hidden still has the ability to scroll on iOS??
Well, Raphael,
this might not be perfect, but it should get you going in the right direction. I tested on Android, my buddy who handles the Apple stuff is unavailable at the moment. Scrolling and other moving is disabled, but you can zoom. One problem, however, is when you are actually in the process of pinch zooming you can move the picture around. You could always snap the picture back to the center after the pinch zoom is complete. (That might even look neat).
Notice I added a method to the jQuery prototype and a property to the jQuery.Event prototype.
/*global console, $*/
/*jslint browser: true*/
(function () {
"use strict";
$.fn.detectPinch = function () {
var numTouches = 0;
// each finger touch triggers touchstart
// (even if one finger is already touching)
$(document).on('touchstart', function (event) {
// if numTouches is more than 1, reset it to 0
// or else you can have numTouches >= 2 when
// actually only one finger is touching
numTouches = (numTouches > 1) ? 0 : numTouches;
// if numTouches is 0 or 1, increment it
numTouches = (numTouches < 2) ? numTouches += 1 : 2;
console.log(numTouches + ' start');
}).on('touchend', function (event) {
// another safety check: only decrement if > 0
numTouches = (numTouches > 0) ? numTouches -= 1 : 0;
console.log(numTouches + ' end');
});
// all event objects inherit this property
$.Event.prototype.isPinched = function () {
return (numTouches === 2) ? true : false;
};
return this;
};
$(document).ready(function (event) {
// call the method we added to the prototype
$(document).detectPinch();
$("#posts").on("click", "img", function (event) {
$(this).css('visibility', 'hidden');
$('#lbBlack').css('top', $(document).scrollTop());
$('#lbImg').attr('src', $(this).attr('src'));
$('#lbBlack').css('width', $(window).width());
$('#lbBlack').css('height', window.innerHeight);
$('#lbBlack').fadeIn(500);
$('#lbImg').css('margin-top', ((window.innerHeight - $('#lbImg').height())) / 2);
document.body.style.overflow = "hidden";
});
$('#lbBlack').on("click", "#lbImg, body", function (event) {
$('#lbBlack').fadeOut(500);
$('#posts img').css('visibility', 'visible');
document.body.style.overflow = "visible";
});
}).on('touchmove', function (event) {
// prevent one-finger movements
if (!event.isPinched()) {
event.preventDefault();
console.log('prevented');
}
});
}());

javascript delay popup

im having a prob with javascript which has been bugging me for hours now. I need to delay a css popup so that if you just scroll mouse around page you wont get loads of popups.
Whatever i try it either makes the popup act goofy, poping up after x seconds with a swipe of any link, auto closing etc etc. if i add a timer to the mouseover it starts acting weird, if i then delete the timer for mouseout it works fine but you can no longer mouseover menu before it closes, also tried adding negative margin and it autocloses
cheers all
javscript
<script type="text/javascript">
var span = document.querySelectorAll('.pop');
for (var i = span.length; i--;) {
(function () {
var t;
span[i].onmouseover = function () {
hideAll();
clearTimeout(t);
this.className = 'popHover';
};
span[i].onmouseout = function () {
var self = this;
t = setTimeout(function () {
self.className = 'pop';
}, 300);
};
})();
}
function hideAll() {
for (var i = span.length; i--;) {
span[i].className = 'pop';
}
};
</script>
css
.pop {
position:relative;
}
.pop div {
display: none;
}
.popHover {
position:absolute;
}
.popHover div {
background-color:#FFFFFF;
border-color:#AAAAAA;
border-style:solid;
border-width:1px 2px 2px 1px;
color:#333333;
padding:5px;
position:absolute;
z-Index:9999;
width:150px;
display: inline-block;
margin-top: -20px;
}
Using jquery might be a little more helpful for what you are trying to do. Try something like this:
// Use a CDN to take advantage of caching
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script type="text/javascript">
var t;
$('.pop').on('mouseover', $.proxy(function () {
hideAll();
clearTimeout(t);
this.addClass('popHover');
this.removeClass('pop');
}, this));
$('.pop').on('mouseout', $.proxy(function () {
var self = this;
t = setTimeout(function () {
self.addClass('pop');
self.removeClass('popHover');
}, 300);
},this));
function hideAll() {
// Since you are calling this from the mouseover function of all the
// elements with the 'pop' class, I dont understand what the purpose of this class
// is so it might not be entirely correct.
$('.pop').addClass('pop');
}
</script>
Let me know if this helps. If you still need it. It would be helpful to have a fiddle to maybe tweak to give you a more accurate response.

Button hiding not functioning properly

HTML Code:
<div id="slick-slidetoggle">wxyz</div>
<div id="slickbox" >abcd</div>​
JavaScript:
var hoverVariable=false;
var hoverVariable2=false;
$('#slickbox').hide();
$('#slick-slidetoggle').mouseover(function() {
hoverVariable2=true;
$('#slickbox').slideToggle(600);
return false;
})
$('#slick-slidetoggle').mouseleave(function() {
hoverVariable2=false;
setTimeout(function (){
if(!hoverVariable && !hoverVariable2){
$('#slickbox').slideToggle(600);
return false;}
}, 1000);
})
$('#slickbox').mouseleave(function() {
hoverVariable=false;
setTimeout(function (){
if(!hoverVariable && !hoverVariable2){
$('#slickbox').slideToggle(600);
return false;}
return false;
}, 1000);
})
$('#slickbox').mouseover(function() {
hoverVariable2=false;
hoverVariable=true;
})​
CSS Code:
#slickbox {
background: black;
width:100px;
height: 135px;
display: none;
cursor:pointer;
color:white;
}
#slick-slidetoggle{
background: yellow;
width:100px;
height: 135px;
cursor:pointer;
color:black;
}
​
Now the desired behaviour is that when mouse is slide over yellow div("wxyz") black div("abcd") should slide down and if mouse is moved out of yellow without moving on to black div, the black div should hide after two seconds.
This is happening. If mouse is moved over black div immediately after moving out of yellow div the black div should not hide as long as the mouse is on the black div. This is also happening.
Next steps are bit difficult to explain but I'll try, when mouse is moved over yellow div and black div comes out then mouse is moved over black div and within two seconds if it moved out of it(black div) then the whole animation goes haywire. Its behaviour is reversed. But if the mouse is kept on black div for more than two seconds and then it is moved out then the whole script runs fine.
This is the link to explain better. http://jsfiddle.net/HAQyK/381/
Try replacing slideToggle() with the appropriate slideUp() and slideDown() calls. http://jsfiddle.net/tppiotrowski/HAQyK/386/
var hoverVariable = false;
var hoverVariable2 = false;
$('#slickbox').hide();
$('#slick-slidetoggle').mouseover(function() {
hoverVariable2 = true;
$('#slickbox').slideDown(600);
return false;
})
$('#slick-slidetoggle').mouseleave(function() {
hoverVariable2 = false;
setTimeout(function() {
if (!hoverVariable && !hoverVariable2) {
$('#slickbox').slideUp(600);
return false;
}
}, 1000);
})
$('#slickbox').mouseleave(function() {
hoverVariable = false;
setTimeout(function() {
if (!hoverVariable && !hoverVariable2) {
$('#slickbox').slideUp(600);
return false;
}
return false;
}, 1000);
})
$('#slickbox').mouseover(function() {
hoverVariable2 = false;
hoverVariable = true;
})​
I re-coded a solution. Checkout the fiddle here
var hideB;
var $black = $('#slickbox');
var $yellow = $('#slick-slidetoggle');
function showBlack() {
if( hideB ) window.clearTimeout( hideB );
$black.stop( true, true );
$black.slideDown(600);
}
function hideBlack() {
hideB = setTimeout( function( ) {
$black.stop( true, true );
$black.slideUp( 600 ); }
, 1000 );
}
$black.hide();
$yellow.mouseenter(function() {
showBlack();
})
$yellow.mouseleave(function() {
hideBlack();
});
$black.mouseleave( function( ) {
hideBlack();
});
$black.mouseenter( function( ) {
showBlack();
});
Your problem seems to be that the slideToggle in firing twice in quick succession because of your duplicate timeout functions. The cleanest way to deal with timeouts or intervals is to store them in a variable to give you the control of removing them when not needed:
// Defined in global scope
var timer;
$('#slick-slidetoggle').mouseleave(function() {
hoverVariable2=false;
// Timer set as function
timer = setTimeout(function (){
if(!hoverVariable && !hoverVariable2){
$('#slickbox').slideToggle(600);
// Timer no longer need and so cleared
clearTimeout(timer);
return false;}
}, 1000);
});
EDIT: Neglected to add the slideUp/slideDown instead of Toggle as per the correct answer above. See the updated jsFiddle which is now correct: http://jsfiddle.net/HAQyK/390/
Another way you could approach your script is to use jQuerys delay funciton and the stop(); method for animation. Wrap the divs in a container and you've got a much simpler block of code:
$('#slick-container').mouseenter(function() {
$('#slickbox').stop().slideDown(600);
}).mouseleave(function(){
$('#slickbox').stop().delay(1000).slideUp(600);
});
Check it out here: http://jsfiddle.net/HAQyK/387/

Categories