I'm trying to make a simple plugin to be able to make a div draggable. Of course a div can have a content inside (typically a form) and it should be dragged with it.
Here is a prototype: http://jsfiddle.net/Xcb8d/539/
The problem is that the texbox inside this div never gets focus, so I cannot type in it:
<div id="draggable-element">Drag me!
<form>
<input type="text" style="width:50px;"/>
<button>ok</button><br><br>
<input type="checkbox">check it</input>
</form>
</div>
As you can see, this does not happen to other elements on the form. Checkbox can be regularily used, as well as the button.
In addition, I would like only the grey coloured area to be draggable, and not the inner elements. On this example, you can place the mouse cursor fully inside the textbox and start the drag of the whole parent div.
the onmousedown handler on the draggable element return false and there by stopping event propagation, allowing it will make the input focus-able and the dragging still works. (at least on chromium)
document.getElementById('draggable-element').onmousedown = function (e) {
// memorize the offset to keep is constant during drag!
x_offset = e.clientX - x_elem;
y_offset = e.clientY - y_elem;
_drag_init(this);
return true;
};
In your onmousedown handler you end with a return false. This prevents the click event from happening when you click on the input, and thus it never gets focus. What I think you are looking for is this:
// Bind the functions...
document.getElementById('draggable-element').onmousedown = function (e) {
if (e.id == 'draggable-element') {
// memorize the offset to keep is constant during drag!
x_offset = e.clientX - x_elem;
y_offset = e.clientY - y_elem;
_drag_init(this);
return false;
}
};
paradoxix's answer was correct in stating that the return falseon the mousedown event is preventing the propagation of all other events.
On any element listening to the mousedown event you won't have any problems, because the event happens for them first, and only after for the box itself. But the input listens to the focusevent, which never happens due to the return false in the box's event.
The solution is to prevent onmousedown from bubbling from the child elements into the box, like so:
var nodes = document.getElementById('draggable-element').childNodes;
for(var i=0; i<nodes.length; i++) {
nodes[i].onmousedown = function(e) {
e.stopPropagation();
}
}
Working fiddle: http://jsfiddle.net/Xcb8d/546/
Related
All,
I have a circle that I drag and drop from the upper div to the bottom div. Please see fiddle. After I click the circle in the bottom I then try to change the height with the input button. The input button is between the top and bottom div and uses incremental values of 5.
However, my event handler doesn't change the height value after incrementing from 5 to 10.
My goal is to allow changing of a height especially after I dropped a circle in the bottom and div and after selecting the circle to simulate activation.
Is there something wrong with how I'm trying to associate the event handler to the circle and the input button?
https://jsfiddle.net/mdevera/ff1bfpsd/
destinationContainer.addEventListener("click", change);
function change(event) {
activeShape = event.srcElement;
}
function changeHeight(event) {
//var height = (parseInt(activeShape.clientHeight, 10) + parseInt(event.target.value, 10)) + "px";
activeShape.style.height = event.target.value + "px";
}
The problem is that your click event listener for the #destination element only calls the change function when it is clicked. In other words, the change function isn't fired when you drag/drop the element since there aren't any click events fired. As a result, the activeShape variable defined in the change function doesn't point to an element since the change function is never called.
Another approach would be to select the last child element of the #destination element using the :last-child pseudo class. The last child is always the last element appended which means that you would always target the most recently dropped element when changing the height.
Updated Example
function changeHeight(event) {
var activeShape = destinationContainer.querySelector(':last-child');
if (activeShape !== null) {
activeShape.style.height = event.target.value + "px";
}
}
After reading your comment:
In addition to the changes above, it also seems like you are looking for the input event rather than the change event. The input event is fired fired synchronously when the value of the element is changed, whereas the change event is fired on blur (which is when focus is removed from that field).
Updated Example
var destinationContainer = document.querySelector("#destination");
var input = document.querySelector('#height-input');
input.addEventListener('input', function (event) {
var activeShape = destinationContainer.querySelector(':last-child');
if (activeShape !== null) {
activeShape.style.height = event.target.value + "px";
}
});
It looks unusual, but you should move out of input after changes made or use keys up/down to "catch up" input changes. In general it's some kind of onblur needed. In modern browser oninput event is preferable one for such cases.
onchange - only after focus out
oninput - the same as onchange but do not need blur focus
onpropertychange -
Just change onchange in your HTML to oninput and your problem will be resolved.
The desired behavior is that a user can click on this object (so it has an ng-click) but if the click turns into a drag before mouseup, then the ng-click handler is not fired.
Perhaps a way to detect, given the $event object which I pass to the handler, whether or not the click was a drag, and have a conditional which checks that in the handler?
(motivation is that the clickable object has text, and I want that text to be able to be highlighted and copied without triggering the click)
using angular version 1.5.6
I think you can set both ng-mousedown and ng-mouseup handlers. When ng-mousedown fires you can save coordinates and then calculate the difference with new coordinates inside ng-mouseup event. If the difference is small - it's a click and you can run you ng-click function.
This is how I managed this - in my controller I have this:
$scope.dragging = false;
$scope.mouseIsDown = false;
$scope.mouseDown = function(event){
//console.log("mouse down");
$scope.mouseIsDown = true;
$scope.dragging = false;
};
$scope.mouseUp = function(event){
//console.log("mouse up");
$scope.mouseIsDown = false;
if($scope.dragging){
//console.log("Must have dragged");
return false;
}else{
//handle single click here, since drag didn't happen
}
};
$scope.mouseMove = function(event){
//console.log("mouse moved");
$scope.dragging = true;
};
In my HTML element I have these handles set, although I'm certain this is what Roman meant:
ng-mousedown="mouseDown(event)"
ng-mouseup ="mouseUp(event)"
ng-mousemove="mouseMove(event)"
I am implementing drag'n'drop functionality on the page. The idea is that user can start dragging image from special place and drop it (release left mouse button) inside textarea, where special tag insertion will occur.
I have no issues with the dragging itself, the problem I got is that I can't determine position of the mouse inside textarea. I know x and y positions from mouseup event and I definetely know the mouse pointer is inside textarea, but can't determine at which position do I need to insert the required tag. Since the focus is not inside the textarea, selectionStart and selectionEnd properties are 0. Also, it is worth noticing that I prevent default behavior by returning false in mousemove handler - otherwise default behavior will put the image itself inside the textarea (which is extremely bad for base64 images).
Below is the code I currently have.
window.onPreviewItemMouseDown = function (e) {
var curTarget = $(e.currentTarget);
var container = curTarget.parent();
window.draggingImage = funcToGetId();
$(document).on("mousemove", window.onPreviewItemDragged);
$(document).on("mouseup", window.onPreviewItemDropped);
};
window.onPreviewItemDragged = function (e) {
return false;
};
window.onPreviewItemDropped = function (e) {
var target = $(e.target);
$(document).off("mousemove");
$(document).off("mouseup");
if (target[0].id === ID_TEXTAREA_TEXT) {
// here I need to get mouse pointer position inside the text somehow
}
return false;
};
Any help is appreciated.
In your case it would be easier to use native ondragstart event instead of a complex mousedown-move-up implementation, and manipulate the data to drop. Something like this:
document.getElementById('img').addEventListener('dragstart', function (e) {
e.dataTransfer.setData("text", '[img]' + this.id + '[/img]');
});
A live demo at jsFiddle. Or a delegated version. Notice that you can also delegate to the "special place" element, which might make the page running a little bit faster.
I want to cancel an HTML5 drag operation based on some condition. I have tried to search this but found nothing useful. It seems that there is no way to cancel an HTML5 drag and drop using JavaScript. returning false in the dragstart doesn't seem to do anything. Any ideas?
You can cancel it by calling event.preventDefault() in the event handler.
For example this should work:
<p id="p1" draggable="true" ondragstart="dragstart_handler(event);">This element is draggable.</p>
<script>
var enableDragDrop = true;
function dragstart_handler(ev) {
if (!enableDragDrop) {
ev.preventDefault();
}
console.log("dragStart");
}
</script>
I think you need to do something tricky to make it work, for I do not know how to stop a drag from js, but you can fake a drag with mousedown,mousedown,mouseup and a isDragging flag
some code look like:
var isDragging = false
var fakeDraggingElement
var currentDragging
$('#item').mousedown(function(e){
e.preventDefault()
isDragging = true
currentDragging = this
//change your cursor and make it loos like dragging
$('#parent').css({cursor: 'move'})
//fake dragging element
fakeDraggingElement = $(this).clone().css({
opacity:0.5,
position:'absolute',
top:e.pageY+offsetY, //parent offset
left:e.pageX+offsetX,//parent offset
}).appendTo(parent)
}).mousemove(function(e){
if(!isDragging){
return
}
fakeDraggingElement.css({
top:e.pageY+offsetY, //parent offset
left:e.pageX+offsetX,//parent offset
})
}).mousedown(function(e){
if(!isDragging){
return
}
cancleDrag()
//your old drop
ondrop(currentDragging,e.target)
})
function cancleDrag(){
isDragging = false
$(currentDragging).remove()
currentDragging = undefined
}
EDIT:
got it. Its a bit roundabout, but works. Take advantage of the fact that the drag event is a cancellable mouse event, and cancelling it stops all further drag actions. Create a drag event on your condition and call preventDefault(). The following cancels it after 3 seconds:
setTimeout(function(){document.getElementById("drag").ondrag = function(e){
e.preventDefault();
this.ondrag = null;
return false;
};},3000);
Note first that I clear all ondrag events after it is cancelled, which you may need to adjust if you need other ondrag events, and second that this truly cancels all parts of the drag, so you don't get an ondragend event after, as shown in this jsfiddle:
http://jsfiddle.net/MaxPRafferty/aCkbR/
Source: HTML5 Spec: http://www.w3.org/html/wg/drafts/html/master/editing.html#dndevents
What I am trying to achieve is changing the way a player moves in a JQuery game.
Rather than a keypress I am trying to get the player to move if they click/mousedown on text, with id "left".
Original code is as follows.
if(jQuery.gameQuery.keyTracker[65]){ //this is left! (a)
var nextpos = parseInt($("#player").css("left"))-5;
if(nextpos > 0){
$("#player").css("left", ""+nextpos+"px");
}
}
My code is as follows, and when I play, it just continuously moves the player left without me pressing anything. It shouldn't move left until I click or mousedown on the "left" id text.
if ($('#left').mousedown){
var nextpos = parseInt($("#player").css("left"))-5;
if(nextpos > 0){
$("#player").css("left", ""+nextpos+"px");
}
}
This:
if ($('#left').mousedown){
will always be true. You're checking if $() has a mousedown property and it always will because all jQuery objects have a mousedown method. You probably want to bind a mousedown event handler:
$('#left').mousedown(function(e) {
//...
});