I'm using YUI to add drag drop support to a div. It also responds to clicks. Unfortunately, the click behavior takes effect even after a drag drop operation. Here's a code snippet:
// Create a DOM object for the group tag.
div = document.createElement('div');
div.className = 'group';
div.onclick = function() { beginEditName(); }
container.appendChild(div);
// Enable drag/drop for the group tag.
dragdrop = new YAHOO.util.DD(div);
dragdrop.scroll = false;
dragdrop.on('dragEvent', function(ev) { onDrag(ev); });
dragdrop.on('endDragEvent', function(ev) { onEndDrag(ev); });
dragdrop.setXConstraint(0,0);
Click is supposed to edit text, while drag drop is supposed to move the tag. However, the onclick event is firing so that text editing begins after the tag is moved.
I could code around the problem, but is there a more direct YUI way of differentiating a simple click from a drag drop?
Michael,
http://ericmiraglia.com/yui/demos/ddclick.php
View the source, and let me know (ericmiraglia at yahoo dot com) if you have any further questions on this.
Modification. I will copy the code here, this way if this guy take off the code from his server people will be able to check the source.
var beingDragged = false;
var dd = new YAHOO.util.DD("drag");
dd.subscribe("mouseDownEvent", function(e){
beingDragged = false;
});
dd.subscribe("startDragEvent", function(e) {
beingDragged = true;
});
dd.subscribe("mouseUpEvent", function(e) {
if(beingDragged) {
alert("dragged")
} else {
alert("clicked");
}
})
Related
I'm looking for a solution to restore a removed attribute. I'm not an experienced programmer, so I'm not sure where to start when sharing my code, so I'll try to give some context below.
I have an image of a map that has several hidden overlays. These overlays are activated by a series of adjacent buttons.
Each of these buttons has a mouseover and mouseout event, which temporarily reveals the overlay. They also have an onclick event that permanently displays the overlay. I've used a .removeAtribute function to remove the mouseout event so that my overlay is permanent.
All other layers are still visible with the mouseover and mouseout events (so that you can make comparisons).
When I onclick another overlay button, it clears the previous one, however, now the mouseout event for the previously selected button is still inactive, so hovering over it causes the overlay to appear permanently.
How can I restore the mouseout event after I've removed it?
I have tried to use .setAttribute("onmouseout"), but I've had no luck in making that work.
Hopefully, this all makes some sense; I'll post some of my code below, which might help give further context.
function btn01On() {
document.getElementById("btn01").removeAttribute("onmouseout");
}
function btnClear() {
document.getElementById("btn01").setAttribute("onmouseout");
}
<button id="btn01" class="map-button map-button1"
onclick="MM_showHideLayers('InfoCurrentExc','','show','OverlayCurrentExc','','show');btn01On();" onmouseover="MM_showHideLayers('OverlayCurrentExc','','show')" onmouseout="MM_showHideLayers('OverlayCurrentExc','','show')">
Current Excavation
</button>
Usually setting up event handlers using onevent attribute, (although oldest method for event handling) is not the recommended way, See https://developer.mozilla.org/en-US/docs/Web/Events/Event_handlers#dom_event_handler_list.
I recommend using EventTarget.addEventListener and EventTarget.removeEventListener for your case. Try the code below
const mouseoutHandler = function() {
MM_showHideLayers('OverlayCurrentExc', '', 'show');
};
const mouseOverHandler = function() {
MM_showHideLayers('OverlayCurrentExc', '', 'show');
};
const button01 = document.getElementById("btn01");
button01.addEventListener("mouseover", mouseOverHandler);
function btn01On() {
if (!mouseoutListener)
button01.addEventListener("mouseout", mouseoutHandler);
}
function btnClear() {
if (mouseoutListener) {
button01.removeEventListener("mouseout", mouseoutListener);
mouseoutListener = null;
}
}
const clickHandler = function() {
MM_showHideLayers('InfoCurrentExc', '', 'show', 'OverlayCurrentExc', '', 'show');
btn01On();
}
button01.addEventListener("click", clickHandler);
I was lucky enough to find someone who had a fix for this problem. I'll share the code below for anyone who might land here with a similar request.
I don't fully understand how this code works so if someone has a good explanation feel free to share it.
// Remove mouse outs
function btn01On() {
document.getElementById("btn01").removeAttribute("onmouseout");
}
// keep mouse outs
const buttonIds = ["btn01"];
const mouseOuts = {};
buttonIds.forEach((id) => {
const el = document.getElementById(id);
if (el) {
mouseOuts[id] = el.getAttribute('onmouseout');
}
});
const restoreMouseOutEvent = () => {
buttonIds.forEach((id) => {
const el = document.getElementById(id);
if (el && mouseOuts[id]) {
el.setAttribute('onmouseout', mouseOuts[id]);
}
});
}
I want to have the click and drag functionality that Raphael.js provides, an example here: https://qiao.github.io/PathFinding.js/visual/.
The way you add and remove obstacles is great, it's essentially combining mousedown event and hover. But how on earth is that done? Any help please?
The closest I have is: https://codepen.io/ProgrammingKea/pen/ZowWJx
The salient bit is
div.addEventListener("mousedown", function(ev){
this.classList.add("obstacle");
});
div.addEventListener("mousemove", function(ev){
this.classList.add("obstacle");
});
div.addEventListener("mouseup", function(ev){
this.classList.add("obstacle");
});
If you press large, then hover over the grid, that's the closest I've got.
But my issue is that its only hover here, I don't have the click functionality the above link does
Please post answers containing only vanilla JS
It maybe feels a bit clunky putting a handler on every element. I'd be tempted to put a handler on the main container and then check from there...
Maybe first add a bit of code to check if mouse is down.
var main = document.getElementById('main')
var mouseDown = 0;
main.onmousedown = function() {
mouseDown=1;
}
main.onmouseup = function() {
mouseDown=0;
}
Then we can check if a mouse is down or over event...
main.addEventListener('mouseover', mousecheck)
main.addEventListener('mousedown', mousecheck)
Then we preventDefault (stop a drag).
If the mouse is down, and the element being acted on is a box, then we'll change it's colour.
function mousecheck( ev ) {
ev.preventDefault();
if( mouseDown && ev.target.className.startsWith( 'box') ) {
ev.target.style.backgroundColor = ev.target.style.backgroundColor == "red" ? 'white' : 'red';
}
}
Codepen
You can use something like:
["mousedown", "mousemove", "mouseup"]
.forEach(function (eve) {
div.addEventListener(eve, function(ev){
this.classList.add("obstacle");
});
});
I have been working on a https://github.com/wekan/ fork where I try to implement additional drag and drop functionality.
Out of the box wekan already came with labels/member drag and drop where the hover overlay is done via droppable hoverClass:
const selector = $cards.find(itemsSelector);
selector.droppable({
hoverClass: 'draggable-hover-card',
accept: '.js-member,.js-label',
'drop'(evt, ui) {
evt.preventDefault();
evt.stopPropagation();
const cardId = Blaze.getData(this)._id;
const card = Cards.findOne(cardId); //get current card I am hovering
//stuff
},
});
I added attachment drag and drop functionality, which won't work exactly like above code and produces the problem that I cannot add the hoverClass cleanly.
When I implement it as shown below the logic of doing addClass is sound but I get an error:
Exception from Tracker afterFlush function:
debug.js:41 Error: Expected DOM element or View
at Object.Blaze.getData (view.js:757)
at list.js:116
at Object.Tracker._runFlush (tracker.js:511)
at onGlobalMessage (setimmediate.js:102)
My code where I want to get an overlay/hover functionality for a single card that I hover over with a file:
const cardId = Blaze.getData(this)._id;
const card = Cards.findOne(cardId); //get current card I am hovering
selector.on({'': (evt) => { //.droppable as above does not work
evt.preventDefault(); //prevent default browser behavior
evt.stopPropagation();
},
dragover(e) { // pulled out of selector.on() to add/removeClass
e.preventDefault();
e.stopPropagation();
card.addClass('is-selected'); //add overlay on hover
},
dragenter(e) {
e.preventDefault();
e.stopPropagation();
},
dragleave(e) {
e.preventDefault();
e.stopPropagation();
card.removeClass('is-selected');
},
drop(evt, ui) {
card.removeClass('is-selected');
if(!ui){ // prevent complications with other drop events
evt.preventDefault();
evt.stopImmediatePropagation(); // prevent x -times actions
evt.target.files = evt.dataTransfer.files; // working file upload
_.each(evt.dataTransfer.files, (f) => {
const file = new FS.File(f);
file.boardId = card.boardId;
file.cardId = card._id;
Attachments.insert(file);
});}
},
});
When I move
const cardId = Blaze.getData(this)._id;
const card = Cards.findOne(cardId);
back into the drop event like in the first code block and try/test e.g. selector.addClass('') instead, the overlay works, but not correctly(selector basically returns the whole card list instead of a single card).
I am a novice currently in training and I just can't understand what error I am making.
Template.currentData() instead of Blaze.getData() does not work.
When I try selector.droppable() my dropped file isn't accepted and all the other events won't fire either.
After trying different varieties I am pretty sure that Blaze.getData() is the problem here. I don't know how to replace its function though.
The problem was a syntax and logic error on my side. While I knew that this in Blaze.getData() would point to the div I wanted, this.addClass() would throw an error. So I thought I'd need the whole Blaze-Block which then lead to the problem above.
Right Syntax:
$(this).addClass('');
I'm using the following code to edit a text in contenteditable div by changing its font. when clicking on a div which is a stylized dropdown menu the selection disappears in chrome. It works fine in firefox, opera and even in IE!
Rangy doesn't find any selected text to deal with. I'm wondering why!
Note: it works fine when using 'mouseover' instead of 'click'
DropDown.prototype = {
initEvents : function() {
var obj = this;
obj.dt.on('click', function(event){
$(this).toggleClass('active');
if (savedSel) {
rangy.removeMarkers(savedSel);
}
savedSel = rangy.saveSelection();
savedSelActiveElement = document.activeElement;
return false;
});
}
}
One workaround is to use the mousedown event rather than the click event and preventing the default behaviour.
obj.dt.mousedown(function(event){
event.preventDefault();
$(this).toggleClass('active');
if (savedSel) {
rangy.removeMarkers(savedSel);
}
savedSel = rangy.saveSelection();
savedSelActiveElement = document.activeElement;
return false;
});
Demo: http://jsfiddle.net/NJuMK/1/
You should save the selection data before the click happens. For example, save it on the mouse over, and use it when the user clicks.
I'm building Yet Another Drag'n'Drop File Uploader. What I'd like to do is highlight the drop area on the page when a drag is started, so the user knows where to move the file to. The interface doesn't make it explicit straight away, which is desired in this case...
Highlighting the dropzone when the user is already over it is fine, but it's the time when the user is dragging but not yet in a droppable area that I'd like to be notified of.
I'm not able to use the dragstart event, since that fires on the HTML Element which is being dragged, and in this case, it's a file from the user's desktop or something. I also tried the following:
$(document.body).on('dragenter', changeTheCSSClass)
.on('dragleave', changeItBackAgain);
However, the enter and leave events fire for every single nested element that the cursor is over, so I get tons of events and the class switches back and forth continuously. Definitely not what I want there.
Any ideas?
As no one posted anything I am posting this, neither stable nor 100% working, but sometimes working.
changeback = 2;
changedback = true;
$(document)
.off('dragenter dragleave')
.on('dragenter', function (e) {
setTimeout(function () {
window.clearTimeout(changeback);
changeback = 0;
console.log('dragenter', e.target);
if (changedback) document.getElementById('nav-askquestion').style.color = '#bc0000';
changedback = false;
}, 10);
})
.on('dragleave', function (e) {
console.log('dragleave', e.target);
if (changeback == 0) {
changeback = window.setTimeout(function () {
console.log('changed back');
document.getElementById('nav-askquestion').style.color = '#FFFFFF';
changedback = true;
}, 15);
}
});
Applied to this page itself. Also you will need to cancel inhouse drags by setting some flag on dragstart.
the names dragenter and dragleave should be actually dragover and dragout it seems :)
How is it going at your end?
You could use angularJS:
<div align="center" class="alert alert-success ng-pristine ng-untouched ng-valid ng-empty"
ngf-select="onPrepareFile($file)" ngf-drop="onPrepareFile($file)" ng-model="vm.file"
name="inputFileStorage"
ngf-pattern="'image/*,application/pdf'"
ngf-accept="'image/*,application/pdf'"
ngf-max-size="10MB">
<a ng-show="!file"><strong>Select or Drop File Here</strong></a>
<a ng-show="file" class="ng-hide"><strong class="ng-binding"> has been Loaded</strong></a>
</div>
In the example I am limiting to a PDF with 10MB max size.
I use TS to code, sorry for the generated code, but can't find original TS...
this.onPrepareFile = function (file) {
if (file) {
var reader = new FileReader();
reader.onloadend = function () {
// yOUR FILE INFO IS IN: reader.result
};
reader.readAsDataURL(file);
}
};