I use only HTML5 drag&drop. I did it already by JQuery UI using but now need to do it by clear HTML5 API.
Like I just said: only dragstart fires. The rest of the events I don't catch. In dragstart function all seems to work correct: event.dataTransfer gets data, I checked it.
Here is the code:
$('#widget')
.attr('draggable', 'true')
.on('dragstart', function(event) {
event.preventDefault();
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData('text/html', $(this).attr('id'))
event.dataTransfer.setDragImage(event.target, 24, 32);
console.log('Im draggable');
console.log(event.dataTransfer.getData('text/html'));
})
.on('dragend', function(event) {
event.preventDefault();
/*$(this).css('top', event.pageX + "px");
event.dataTransfer.getData('text/html');*/
console.log('dragend');
});
$('#widget_dest')
.click(function(event) {
console.log("click widget_dest");
})
.on('dragenter', function(event) {
event.preventDefault();
console.log("dragenter");
})
.on('dragover', function(event) {
event.preventDefault();
console.log("dragover");
})
.on('drop', function(event) {
event.preventDefault();
event.stopPropagation();
console.log("drop");
var data = event.dataTransfer.getData('text/html');
$(this).append($('#' + data));
$('#' + data).css('top', event.pageX + 'px');
});
});
The only logs I get are: dragstart (correct data) and of the click function.
I purposely inserted click finction to check Widget_dest's properties correctness. Click event fires, the rest of events not.
I'll be very thankful for any help
Victor
You should not be using event.preventDefault() in dragstart. This example works fine: http://jsfiddle.net/noziar/Q3eh3/4/
Also, even if I removed event.preventDefault() in most places, note that in dragover it is necessary, otherwise drop may not fire (at least in Chrome).
As a sidenote, I'm not sure how you're able to read/set properties on event.dataTransfer, since the jQuery event does not have the dataTransfer property - you can use originalEvent for that.
Here is my code:
HTML:
<div id="widget">I'm a widget</div>
<div id="widget_dest">
<span id="widget_box_title">Drag widgets into this box</span>
</div>
CSS:
#widget_dest {
position:absolute;
top: 40px;
border-style:solid;
border-width:1px;
}
#widget {
color:green;
}
#widget_box_title {
color:red;
}
JS:
$('#widget')
.attr('draggable', 'true')
.on('dragstart', function(event) {
var original = event.originalEvent;
original.dataTransfer.effectAllowed = "move";
original.dataTransfer.setData('Text', $(this).attr('id'))
original.dataTransfer.setDragImage(event.target, 24, 32);
console.log('Im draggable');
console.log(original.dataTransfer.getData('Text'));
})
.on('dragend', function(event) {
$(this).css('top', event.pageX + "px");
event.originalEvent.dataTransfer.getData('Text');
console.log('dragend');
});
$('#widget_dest')
.click(function(event) {
console.log("click widget_dest");
})
.on('dragenter', function(event) {
console.log("dragenter");
})
.on('dragover', function(event) {
event.preventDefault();
console.log("dragover");
})
.on('drop', function(event) {
console.log("drop");
var data = event.originalEvent.dataTransfer.getData('Text');
$(this).append($('#' + data));
});
Related
I just have a problem with drop zone for my photos, dataTransfer doesn't work end return as undefault. Please help me and show where I made a mistake. Thank you.
jQuery(document).ready(function ($) {
$('#dropbox').on("dragenter dragstart dragend dragleave dragover drag drop", function (e) {
e.preventDefault();
});
$('#dropbox').on('drop', function(e){
e.preventDefault();
var files = e.dataTransfer.files;
console.log(files);
if(files.length>1) console.log("noooo!");
else
{
console.log("Drop!");
}
});
});
div {
width:600px;
height:300px;
background:#FFFCCC;
}
<html>
<body>
<div id="dropbox"></div>
</body>
</html>
Since you are using jQuery, the event you have (inside the drop) is not the original event, but a jQuery wrapper.
You can use the e.originalEvent to get the original event, and there you have the dataTransfer.files:
e.originalEvent.dataTransfer.files
Here is a working example:
jQuery(document).ready(function ($) {
$('#dropbox').on("dragenter dragstart dragend dragleave dragover drag drop", function (e) {
e.preventDefault();
});
$('#dropbox').on('drop', function(e){
e.preventDefault();
debugger;
var files = e.originalEvent.dataTransfer.files;
console.log(files);
if(files.length>1) console.log("noooo!");
else
{
console.log("Drop!");
}
});
});
div {
width:600px;
height:300px;
background:#FFFCCC;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="dropbox"></div>
I'm trying to prevent scrolling of my site until a button is clicked and then allow scrolling. I can successfully prevent the scrolling but then I can't unbind this function. Here is my code:
Thanks in advance!
$('body').addClass('noscroll');
$('.fold-trigger').click(function(event) {
$('body').removeClass('noscroll')
console.log('removed');
});
if ($('body').hasClass('noscroll')){
$(window).bind('scroll', function(){
$('body').on({
'mousewheel': function(e) {
if (e.target.id == 'el') return;
e.preventDefault();
e.stopPropagation();
}
})
});
} else {
$('body').on({
'mousewheel': function(e) {
if (e.target.id == 'el') return;
e.preventDefault(false);
e.stopPropagation(false);
}
})
}
}
You should change your logic if you want to toggle this class on your body.
$('body').on('mousewheel', function(e) {
if ($('body').hasClass('noscroll')) {
e.preventDefault();
e.stopPropagation();
}
});
Then add and remove noscroll class whenever you wish.
How to prevent click event on anchors (redirects to href url, in demo redirecting to google) inside some wrapper when making touch and mouse drag events on this wrapper? With preventDefault and stopPropagation I can only limit bubbling up the DOM, right?
I want to disable links when dragging, and enable click while not dragging.
Here's demo with the problem.
var items = $('#items');
function start(event) {
event.preventDefault();
console.log('touchstart mousedown');
items.on('touchmove mousemove', move);
items.on('touchend mouseup', end);
return false;
}
function move(event) {
console.log('touchmove mousemove');
return false;
}
function end(event) {
console.log('touchend mouseup');
items
.off('touchmove mousemove')
.off('touchend mouseup');
return false;
}
items.on('touchstart mousedown', start);
https://jsfiddle.net/9oxr4quz/4/
I came up with two solutions:
First:
when the mousemove event is triggered, bind a click event and call preventDefault on the event object, to prevent the browser to follow the link. Turn off the jquery click handlers when the touchstart and/or mousedown are triggered.
Javascript:
var items = $('#items a'); // Notice I changed the selector here
function start(event) {
event.preventDefault();
console.log('touchstart mousedown');
items.off('click');
items.on('touchmove mousemove', move);
items.on('touchend mouseup', end);
return false;
}
...
function move(event) {
items.on('click', function(event){ event.preventDefault(); });
console.log('touchmove mousemove');
return false;
}
...
Working demo: http://codepen.io/anon/pen/WvjrPd
Second:
Handle the click events by yourself, that way you can decide when the browser should visit another site and when should do nothing. This can be achieved by replacing the href attribute by a data-link or data-href attribute.
Now, when the touchstart or mousedown events are triggered, turn on the click events; if any of those events lead to a mousemove event, turn off the click events:
HTML:
<div id="items" class="items">
<div class="item">
<a data-link="http://google.com">Anchor</a>
</div>
<div class="item">
<a data-link="http://google.com">Anchor</a>
</div>
</div>
Javascript:
var items = $('#items a'); // Notice I changed the selector here
function start(event) {
event.preventDefault();
console.log('touchstart mousedown');
items.on('click', click); // Turn on click events
items.on('touchmove mousemove', move);
items.on('touchend mouseup', end);
return false;
}
function move(event) {
items.off('click'); // Turn off click events
console.log('touchmove mousemove');
return false;
}
...
function click(event) { // Visit the corresponding link
var link = $(this).attr('data-link');
alert('Visit link: ' + link);
// window.location.href = link;
}
items.on('touchstart mousedown', start);
CSS:
.item {
background-color: gray;
}
.item + .item {
margin-top: 10px;
}
.item a {
cursor: pointer;
display: inline-block;
background-color: blue;
color: white;
padding: 9px;
width: 100%;
height: 100%;
}
Working demo: http://codepen.io/anon/pen/EjmPrJ
What about a CSS approach with JavaScript of using disabling all pointer events when touching or or move then add them back. A quick one is define CSS like this:
a.prevent-me {
pointer-events: none; /* This line */
cursor: default;
}
the using jquery add the class and remove the class like below as needed in your events.
...
$(".item a").addClass("prevent-me");
...
...
$(".item a").removeClass("prevent-me");
...
So the whole solution, I havent tested it might be
var items = $('#items');
function start(event) {
event.preventDefault();
console.log('touchstart mousedown');
$(".item a").addClass("prevent-me"); //remove all click events
items.on('touchmove mousemove', move);
items.on('touchend mouseup', end);
return false;
}
function move(event) {
console.log('touchmove mousemove');
return false;
}
function end(event) {
console.log('touchend mouseup');
items
.off('touchmove mousemove')
.off('touchend mouseup');
$(".item a").removeClass("prevent-me"); //enable back click
return false;
}
items.on('touchstart mousedown', start);
I have a menu where user clicks on link and list appears via .addClass( "show-nav" ).
Here is jsFiddle with JS code:
jQuery(".nav-js-trigger").each(function(){
this.onclick = function() {
var hasClass;
hasClass = jQuery(this).next().hasClass( "show-nav" );
jQuery('.show-nav').removeClass('show-nav');
if (hasClass === false) {
jQuery(this).next().addClass( "show-nav" );
}
}
});
I want to remove the class show-nav if the user clicks outside of the div with class show-nav. How do I do this?
I have seen examples of e.target div ID but not class, particularly not a scenario like this.
You can add an listener to the element with an event.stopPropagation() on it, and another listener to the body, to capture this event if not intercepted before.
Something like this:
$(".show-nav").on("click", function(event){
event.stopPropagation();
});
$("body").on("click", function(event){
$(".show-nav").hide(); // or something...
});
To simplify your use-case, here is a JSFiddle.
$(".trigger").on("click", function(event)
{
$(".menu").toggle();
event.stopPropagation();
});
$(".menu").on("click", function(event)
{
event.stopPropagation();
});
$(document).on("click", function(event)
{
$(".menu").hide();
});
.menu
{
display: none;
background: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
menu
<div class="menu">Hello</div>
$(document).on("click", function(e) { if ($(e.target).is(".trigger") === false) {
$(".menu").hide();
}
});
I have a draggable <div> with a click event and without any event for drag,
but after I drag <div> the click event is apply to <div>.
How can prevent of click event after drag?
$(function(){
$('div').bind('click', function(){
$(this).toggleClass('orange');
});
$('div').draggable();
});
http://jsfiddle.net/prince4prodigy/aG72R/
FIRST attach the draggable event, THEN the click event:
$(function(){
$('div').draggable();
$('div').click(function(){
$(this).toggleClass('orange');
});
});
Try it here:
http://jsfiddle.net/aG72R/55/
With an ES6 class (No jQuery)
To achieve this in javascript without the help of jQuery you can add and remove an event handler.
First create functions that will be added and removed form event listeners
flagged () {
this.isScrolled = true;
}
and this to stop all events on an event
preventClick (event) {
event.preventDefault();
event.stopImmediatePropagation();
}
Then add the flag when the mousedown and mousemove events are triggered one after the other.
element.addEventListener('mousedown', () => {
element.addEventListener('mousemove', flagged);
});
Remember to remove this on a mouse up so we don't get a huge stack of events repeated on this element.
element.addEventListener('mouseup', () => {
element.removeEventListener('mousemove', flagged);
});
Finally inside the mouseup event on our element we can use the flag logic to add and remove the click.
element.addEventListener('mouseup', (e) => {
if (this.isScrolled) {
e.target.addEventListener('click', preventClick);
} else {
e.target.removeEventListener('click', preventClick);
}
this.isScrolled = false;
element.removeEventListener('mousemove', flagged);
});
In the above example above I am targeting the real target that is clicked, so if this were a slider I would be targeting the image and not the main gallery element. to target the main element just change the add/remove event listeners like this.
element.addEventListener('mouseup', (e) => {
if (this.isScrolled) {
element.addEventListener('click', preventClick);
} else {
element.removeEventListener('click', preventClick);
}
this.isScrolled = false;
element.removeEventListener('mousemove', flagged);
});
Conclusion
By setting anonymous functions to const we don't have to bind them. Also this way they kind of have a "handle" allowing s to remove the specific function from the event instead of the entire set of functions on the event.
I made a solution with data and setTimeout. Maybe better than helper classes.
<div id="dragbox"></div>
and
$(function(){
$('#dragbox').bind('click', function(){
if($(this).data('dragging')) return;
$(this).toggleClass('orange');
});
$('#dragbox').draggable({
start: function(event, ui){
$(this).data('dragging', true);
},
stop: function(event, ui){
setTimeout(function(){
$(event.target).data('dragging', false);
}, 1);
}
});
});
Check the fiddle.
This should work:
$(function(){
$('div').draggable({
start: function(event, ui) {
$(this).addClass('noclick');
}
});
$('div').click(function(event) {
if ($(this).hasClass('noclick')) {
$(this).removeClass('noclick');
}
else {
$(this).toggleClass('orange');
}
});
});
DEMO
You can do it without jQuery UI draggable. Just using common 'click' and 'dragstart' events:
$('div').on('dragstart', function (e) {
e.preventDefault();
$(this).data('dragging', true);
}).on('click', function (e) {
if ($(this).data('dragging')) {
e.preventDefault();
$(this).data('dragging', false);
}
});
You can just check for jQuery UI's ui-draggable-dragging class on the draggable. If it's there, don't continue the click event, else, do. jQuery UI handles the setting and removal of this class, so you don't have to. :)
Code:
$(function(){
$('div').bind('click', function(){
if( $(this).hasClass('ui-draggable-dragging') ) { return false; }
$(this).toggleClass('orange');
});
$('div').draggable();
});
With React
This code is for React users, checked the draggedRef when mouse up.
I didn`t use click event. The click event checked by the mouse up event.
const draggedRef = useRef(false);
...
<button
type="button"
onMouseDown={() => (draggedRef.current = false)}
onMouseMove={() => (draggedRef.current = true)}
onMouseUp={() => {
if (draggedRef.current) return;
setLayerOpened(!layerOpened);
}}
>
BTN
</button>
I had the same problem (tho with p5.js) and I solved it by having a global lastDraggedAt variable, which was updated when the drag event ran. In the click event, I just checked if the last drag was less than 0.1 seconds ago.
function mouseDragged() {
// other code
lastDraggedAt = Date.now();
}
function mouseClicked() {
if (Date.now() - lastDraggedAt < 100)
return; // its just firing due to a drag so ignore
// other code
}