I am creating a drag-&-drop function on a WYSIWYG component (based on Medium-Editor), works fairly ok, except on firefox where it seems to have some issues.
The feature show allow the user to drag some "widgets" (basically just divs) and change its place, the mechanisms is based on using the dataTransfer of native JS drag-&-drop, then, once the user is ready to drop above a given paragraph (because there's a lot of text around), the widget is added after the paragraph.
One issue in particular remains perplexing: on drop, for Firefox, the paragraph splits right where the cursor of the mouse was pointing, hence creating two new paragraph elements from one old and unique paragraph elements. In Chrome, it works just fine. Using the After method, that's should be the nominal behavior.
The core of the code is as follows:
Draggable widgets:
// Drag event listener
widgetElement.addEventListener('dragstart', event => {
// #ts-ignore
document.dragSourceElement = widgetElement
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.dropEffect = 'move'
event.dataTransfer.setData('text/html', widgetElement.outerHTML)
})
"Target" paragraphs:
// Drop event listener
paragraphElement.addEventListener('drop', _ => {
if (document.dragSourceElement && document.dragSourceElement !== paragraphElement) {
if (this.detectBrowser() === 'chrome') {
paragraphElement.after(document.dragSourceElement)
}
// Treat Firefox singularly since it interprets "move" in drag&drop as "copy" (For some reason) through the use of a isParagraph flag.
if (this.detectBrowser() === 'firefox') {
paragraphElement.after(document.dragSourceElement)
// isParagraphe is true, remove the old dragged widget and create a new one on the drop position. isParagraph is set in an argument of a higher order method.
if (isParagraphe) {
document.dragSourceElement.remove()
}
}
}
return false
})
Here's a gif of what happens on chrome:
Here's a gif of what happens on firefox:
Ok, so why the difference ? It might juste be difference in implementation of Medium-editor on the different browsers, but I suspect Firefox manages the events differently.
What would be the cleanest way to harmonize the behavior and make sure I get a chrome-like behavior in Firefox (and let's try to avoid, unless necessary, parsing the whole text to reconstitute old states of paragraphs xD).
Thanks.
Related
I am developing a Tizen Webapp for Watches with bezel input.
To listen for the Bezel rotation event on the HTML document I used the following code:
rotaryDetentCallback = function rotaryDetentHandler(e) {
var direction = e.detail.direction;
if (direction === "CW") {
alert("<-");
} else if (direction === "CCW") {
alert("->");
}
};
document.addEventListener("rotarydetent", rotaryDetentCallback, false);
console.log(document.hasFocus());//returns true
$('select').on('change', function(){
console.log(document.hasFocus()); //Prints false
document.activeElement.blur(); //doesnt work
window.focus(); //doesnt work
document.focus(); //retuns error
});
The rotarydetent listener works great but after I change any Select element in the App at runtime, the document loses focus and the event is not firing any more.
How can I get the focus back to document object after select change?
PS: the standard UI for Select Tag on Tizen wearable web-apps uses the bezel also, so I guess it takes the eventListener from document and doesn't give it back.
There is no such function document.focus() and window.focus() is for restoring the focus to the current window (apparently if you have multiple ones open) and not for bringing it back to the contents of the page.
Try to focus document.body or its focusable ancestor instead - the one being a form field, or a link or having an appropriate tabIndex attribute.
I have a div that is draggable. The "splitter" that is used for dragging it / toggling it, it's supposed to allow expanding and collapse only on doubleclick. (Or simple drag and expand).
The functionality works fine, but once the div is in the collapsed state, it repositions/opens up to some 10px width, when just 'clicking' on the splitter. I.e, single click/mousedown.
I have tried stopEvent, return false, also all other possibilities of fixing it, but it won't stop from expanding on single click
This is not supposed to happen. Any help would be appreciated.
To solve this problem I think you will need to add some extra state to your DOM node containing a flag if the object moved or not. If it really moved, then the click event should not be fired.
An example of adding the state:
lang.mixin(domNode, {
moved: false
});
Then, when the Move event is fired, you set the flag to true, for example:
moveable.on("Move", function(mv, pos, evt) {
if (evt.target.moved === false) {
console.log("Drag detected");
}
evt.target.moved = true;
});
In the click event handler you will have to verify if the flag is changed to true or not and put it back to false (for the next moves). For example:
on(domNode, "click", function(evt) {
if (evt.target.moved === false) {
// Execute your logic here
}
});
Of course, this isn't the most elegant solution (but it works). The most beautiful solution would be that you extend the Moveable yourself and make it work to your needs.
I tested it out with a JSFiddle, which you can see here.
Using the dragenter event I show a dropzone on the webpage to have dropped files upload quickly, and it all works well. However, the dropzone also pops up when dragging selected text. How to tell the difference early on?
I know that the drop event exposes all file contents to be iterated using dataTransfer.files, but that's too late. I need it at dragenter, only I see the files array to be empty at all times.
I need full control over look & feel I am not looking for an existing lib.
I can see different values for event.dataTransfer.Clipboard.effectAllowed when dragging text versus files, but values also differ per browser (Chrome vs FF).
MooTools is in place, if that helps.
Okay, I made enough progress to get differentiating working in Chrome and Firefox (3.6+). It's probably not foolproof but in case anyone might find it useful, here's the code:
var isFileTransfer = false;
if (evt.dataTransfer.types) {
for (var i=0; i<evt.dataTransfer.types.length; i++) {
if (evt.dataTransfer.types[i] == "Files") {
isFileTransfer = true;
break;
}
}
}
I needed to determine if a file was being dragged into the browser from outside, versus an image being dragged from within the browser window. I did it by listening for dragstart on the document object. When a file is dragged into the browser from outside, the dragstart doesn't fire. So, if it does fire, it means something within the same page is being dragged.
document.addEventListener('dragstart', function() {
samePageDrag = true;
}, false);
document.addEventListener('dragend', function() {
if (samePageDrag) {
samePageDrag = false;
}
}, false);
With this, you can check the value of samePageDrag after a dragenter or dragover event to determine if the thing being dragged is from outside the browser or not.
See docs here: https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types
If any files are included in the drag operation, then one of the types will be the string Files.
This is a one line solution
// returns true if dragged item is not a file
// returns false if dragged item is a file
event.dataTransfer.types.indexOf('Files') === -1
// returns true if dragged item is a file
// returns false if dragged item is not a file
event.dataTransfer.types.indexOf('Files') !== -1
I wrote a small function to detect files dragging.
function isFileTransfer(evt){
return [].some.call(evt.dataTransfer.types,function(t){return t=="Files";});
}
I am looking to do the following
Disallow text selection inside of an input element
Do not show the cursor carrot inside of an input
i cannot simply blur the input on click, focus must remain in the input. There is probably a way to do this in just IE, i would of course rather have a cross browser solution but ill settle for IE (or FF) only solution.
Here is a demo page where you can see why i might need this functionality:
http://programmingdrunk.com/current-projects/dropdownReplacement/
if you click on the dropdowns in the first row on page, you will see the carrot inside the dropdown which looks funny. (this wont happen in chrome, but will in FF or IE)
A bit hacky, but:
onclick="this.selectionStart=this.selectionEnd=-1;"
onselect="this.selectionStart=this.selectionEnd=-1;"
Seems to work in Firefox (3.6.3).
Do us all a favor and hide it from the HTML, though (attachEvent magic).. And don't tell anyone I suggested this :)
I use the following function in my code, it's not JQuery but it should be fairly easy to convert:
function disableSelection(elm) {
// Disable the selection of `elm` - should work for all major browsers except Opera
// which doesn't seem to allow disabling selections unless the mousedown
// events etc are cancelled as far as I know
// Disable the select start event
elm.onselectstart = function() {
return false
}
// Disable in IE - see http://msdn.microsoft.com/en-us/library/ms534706(VS.85).aspx
elm.unselectable = "on"
// Disable in Mozilla - see https://developer.mozilla.org/en/CSS/-moz-user-select
elm.style.MozUserSelect = 'none'
// Disable Safari/Chrome
// See http://help.dottoro.com/lcrlukea.php
elm.style.webkitUserSelect = 'none'
// Disable in other browsers
elm.style.userSelect = 'none'
// Display a normal cursor
elm.style.cursor = "default"
}
You can disable text selection in some non IE browsers with CSS user-select.
-webkit-user-select:none;
-k-user-select:none;
-moz-user-select:moz-none;
user-select:none;
Not sure about IE.
As far as the effect you're trying to achieve, how about making the input invisible and have a div on top of the input that displays the value of the input?
If you need to change the value in the input, a click event on the div would direct focus to the input, and a keypress event would update the div.
Haven't tried it, but seems like it should work.
EDIT:
Use CSS to render the input invisible in order to retain tabbing functionality.
(Example assumes background is #FFF)
#myInput {
border-width:0;
color:#FFF;
background:#FFF;
outline:0;
}
In IE, for example, when you press the left button on an image and keeping it pressed try to move the mouse, the drag n' drop action is taking place; how could I prevent this default action so that doing that way nothing will happen. I am building an image cropper, so you should understand why I need that. I am not much interested in knowing how to do so with help of jQuery or the like. As I study JavaScript, I prefer coding in plain-vanilla JS. It is important for me to learn how to make it cross-browser if there are any differences for such a thing.
Just like August's, but plain JS:
var imgs = document.getElementById("my_container")
.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
imgs[i].onmousedown = function () {
return false;
};
}
If you want to do it 'new-style', google for 'addEventListener()' (all browsers but...) and 'attachEvent()' (...IE) methods.
Here's one in jQuery:
$("#my_container img").mousedown(function () {
return false;
});
http://www.google.com/search?q=cross+browser+event+hooking will probably teach you everything you need to know about cross browser event hooking. I don't know how to hook events without a framework, because that's an edge case IMHO. In The Real World (tm), you'll always use a framework.
The core here is that you have to stop the mousedown event from running. This will make drag and drop impossible, if you hook the event on text you won't be able to select that text, and so on.
If you're building an image cropper, you're going to put some kind of overlay on the image, probably a relatively or absolutely positioned div, inside of which you will "draw" a rectangle when the user clicks, holds and drags. This will make it impossible for the user to drag the image itself, so no fix for that is needed.
Even if you do not use an overlay, you are still going to hook the mousedown event - there is no other way to implement a JS cropper as far as I know. Hooking that event will by itself be enough to prevent the browser from initiating a drag and drop action.
I'm using code similar to the following to prevent dragging, which has the advantage of targetting actual drag-related events rather than the generic mousedown (which could conceivably have side-effects). Works in all the mainstream browsers except Opera.
function cancellingEventHandler(evt) {
evt = evt || window.event;
if (evt.preventDefault) {
evt.preventDefault();
} else if (typeof evt.returnValue !== "undefined") {
evt.returnValue = false;
}
return false;
}
function disableDragging(node) {
node.ondragstart = cancellingEventHandler;
node.ondraggesture = cancellingEventHandler;
}
disableDragging( document.getElementById("anImage") );