I have a list of eventlistners for a drag'n drop feature.
td.addEventListener('dragenter', onDragEnter);
td.addEventListener('dragleave', onDragLeave);
td.addEventListener('dragover', onDragOver);
td.addEventListener('drop', onDrop);
In firefox, the drop event is not fired when an item is dropped. The functions called for dragenter, dragover and dragleave each prevent default action (copied from http://mereskin.github.io/dnd/):
const onDragEnter = e => {
onDragOver(e);
console.log('onDragEnter:');
if(e.target.nodeType == 1) {
e.target.className = 'drop-over';
} };
const onDragOver = e => {
console.log('onDragOver:');
if(e.target.nodeType == 1) {
e.dataTransfer.dropEffect = e.target.getAttribute('data-drop-effect');
e.preventDefault();
e.stopPropagation();
} };
On http://mereskin.github.io/dnd/ all events seem to work (tried to have same methods, eventlisteners, etc).
The draggable item is construced with the specific attribute:
let img = document.createElement('img');
img.src = `icons/${obj._image}`;
img.setAttribute('draggable','true');
In Chrome it works perfectly, on my and the linked website.
Edit: Tried Edge as well, works fine. Tried also FF on another machine, same result. Created a jsfiddle for demo: https://jsfiddle.net/as965dkz/6/
As I found out, Firefox requires that dataTransfer.setData() is set at the dragstart event. Afterwards, on the drop event e.preventDefault(); must be called.
Related
Using some basic drag'n'drop features and i keep getting this message in Firefox, works well in Chrome though.
NoModificationAllowedError: Modifications are not allowed for this document
This happens when i try to use the clearData() function on the event when the ondrop event is fired.
HTML:
<!-- draggable element -->
<div draggable="true" ondragstart="onDragStart(event);">
<!-- dropzone element -->
<div ondragover="onDragOver(event);" ondragenter="onDragEnter(event);" ondrop="onDrop(event);"></div>
JS:
function onDragStart(event) {
event
.dataTransfer
.setData('text/plain', 'test');
}
function onDragOver(event) {
event.preventDefault();
}
function onDragEnter(event) {
event.preventDefault(); // polyfill fix
}
function onDrop(event) {
event.preventDefault();
event
.currentTarget
.parentNode
.classList.add("dropped");
event
.dataTransfer
.clearData(); //<---- This is where it fails
}
So yeah no idea how to fix this, and i need to clear the data as i set/reset some variable and internal params using it (code omitted). Works well in chrome (even IE)
Use Cleardata() method in OnDragStart and not use it in OnDrop() Method.
onDragStart(event) {
event.dataTransfer.clearData();
event
.dataTransfer
.setData('text/plain', event.target.dataset.item);
}
Must user text/plain.
Must use preventDefault() while dropping.
onDragOver(event) {
event.preventDefault();
}
Must use preventDefault inside onDrop Method like that.
onDrop(event) {
const id = event
.dataTransfer
.getData('text');
const draggableElement = '';
event.preventDefault();
this.template.querySelectorAll('.example-draggable').forEach(element => {
if (element.innerHTML == id) {
this.draggableElement = element;
}
});;
if ((!event.target.innerText.includes('Drag From Here')) && (!event.target.innerText.includes('Drop Here'))) {
const bar = event.target.parentElement;
bar.appendChild(this.draggableElement);
}
else {
const bar = event.target;
bar.appendChild(this.draggableElement);
}
}
here i use preventdefault in middle of the code that will prevent reloading of the page while appending element.
I want to handle mouse right-click event for my button. I wrote the following code;
mybutton.onmousedown = e => {
e.preventDefault();
const mouseEvent = {
0: () => leftClickCallback,
2: () => rightClickCallback
}
mouseEvent[ e.button ]();
}
It works fine but it doesn't prevent the browser context menu and I have to set the "oncontextmenu" event like below to prevent the browser context menu event;
mybutton.oncontextmenu = e => e.preventDefault();
I've also tried to stop propagation of mouse event like below although it didn't work:
mybutton.onmousedown = e => {
e.preventDefault();
e.stopPropagation(); // <====
const mouseEvent = {
0: () => leftClickCallback,
2: () => rightClickCallback
}
mouseEvent[ e.button ]();
}
I am wondring why I need to explicitly disable oncontextmenu event for my button.
The right mouse button click seems to fire multiple events (though it might depend on the browser) :
a MouseDown event, with event.button === 2 and/or event.which === 3,
a ContextMenu event.
It makes sense since the context menu can also be opened by a keyboard button (depending on your keyboard layout), or a macro.
What you can do is use the same callback. For example :
function preventAll(event) {
event.preventDefault();
event.stopPropagation();
}
document.getElementById('something').addEventListener('mousedown', preventAll);
document.getElementById('something').addEventListener('contextmenu', preventAll);
<button id="something">test</button>
How make a cross-browser and cross-platform click on a document? I have tryed:
var clickEvent = function (e) {
console.log(123);
};
var body = document.getElementsByTagName('body')[0];
body.onclick = clickEvent;
document.addEventListener("click", clickEvent);
window.addEventListener("click", clickEvent);
body.addEventListener("click", clickEvent);
But in different browsers it works few times. How to make it work only once?
The best one to use is
document.addEventListener("click", clickEvent);
Remove the other ones. It's running many times because you handle the click event on multiple elements (window, document and document.body). They all trigger because all clicks inside the viewport trigger click events on those objects. Your code should look like this:
var clickEvent = function (e) {
console.log(123);
};
document.addEventListener("click", clickEvent);
EDIT: If you want maximum compatibility, use onclick:
var clickEvent = function (e) {
console.log(123);
};
document.onclick = clickEvent;
There could be issue with IE browsers with addEventListener() as some of the old internet explorer browsers doesn't have implementation of it but it has attachEvent. So, you might look to bind it too and app browsing is not limited to desktop devices only. There might be the user is on mobile devices so you might make use of touch events like touchstart/touchend etc. :
var clickEvent = function(e) {
console.log(itHas, 123);
};
var body = document.body; // get the document's body
var itHas = document.implementation.hasFeature(addEventListener), // check for the
docClick, // feature implementation.
ev = navigator.userAgent.indexOf(/mobi/g) !== -1 ? "touchstart" : "click";
// above will use an event based on user agent like for mobile it will use touch event
// and for desktop/laptops click event.
if(itHas){
docClick = body.addEventListener;
}else{
docClick = body.attachEvent;
}
docClick(ev, clickEvent);
So this is just a small personal project that I'm working on using awesomium in .net. So in awesomium I have this browser open and all that and I want to click this button that has this code.
<a class="buttonright" > Bump </a>
But considering it's a class and not a button I'm having trouble finding a way to "click" it. My plan is to use javascript in awesomium to click it but maybe I'm approaching this from the wrong direction?
Thanks
Update:
After a lot of comments (back and forth) I set up a fiddle, with a working version of this code (the code here works, too, but needed some debugging). The eventTrigger function in the fiddle has been stripped of all comments, but I've added an example usage of this function, which is generously sprinkled with comments.
Browse through it, fork it, play around and get familiar with the code and concepts used there. Have fun:
Here's the fiddle
If by "finding a way to click it" you mean: how to programmatically click this anchor element, then this is what you can use:
Here's a X-browser, slightly verbose yet comprehensive approach:
var eventTrigger = function(node, event)
{
var e, eClass,
doc = node.ownerDocument || (node.nodeType === (document.DOCUMENT_NODE || 9) ? node : document);
//after checking John Resig's Pro JavaScript Techniques
//the statement above is best written with an explicit 9
//Given the fact that IE doesn't do document.<NODE_CONSTANT>:
//doc = node.ownerDocument || (node.nodeType === 9 ? node : document);
if (node.dispatchEvent)
{//dispatchEvent method is present, we have an OK browser
if (event === 'click' || event.indexOf('mouse') >= 0)
eClass = 'MouseEvents';//clik, mouseup & mousedown are MouseEvents
else
eClass = 'HTMLEvents';//change, focus, blur... => HTMLEvents
//now create an event object of the corresponding class
e = doc.createEvent(eClass);
//initialize it, if it's a change event, don't let it bubble
//change events don't bubble in IE<9, but most browsers do
//e.initEvent(event, true, true); would be valid, though not standard
e.initEvent(event, !(event === 'change'), true);
//optional, non-standard -> a flag for internal use in your code
e.synthetic = true;//mark event as synthetic
//dispatch event to given node
node.dispatchEvent(e, true);
//return here, to avoid else branch
return true;
}
if (node.fireEvent)
{//old IE's use fireEvent method, its API is simpler, and less powerful
//a standard event, IE events do not contain event-specific details
e = doc.createEventObject();
//same as before: optional, non-standard (but then IE never was :-P)
e.synthetic = true;
//~same as dispatchEvent, but event name preceded by "on"
node.fireEvent('on' + event, e);
return true;//end IE
}
//last-resort fallback -> trigger any directly bound handler manually
//alternatively throw Error!
event = 'on' + event;
//use bracket notation, to use event's value, and invoke
return node[event]();//invoke "onclick"
};
In your case, you can use this function by querying the DOM for that particular element, like so:
var elem = document.querySelector('.buttonright');//IE8 and up, will only select 1 element
//document.querySelectorAll('.buttonright'); returns a nodelist (array-like object) with all elements that have this class
eventTrigger(elem, 'click');
That should have the effect of clicking the anchor element
If you're looking for a way to handle click events on this element (an anchor that has a buttonright class), then a simple event listener is all you need:
document.body.addEventListener('click', function(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
if (target.tagName.toLowerCase() === 'a' && target.className.match(/\bbuttonright\b/))
{//clicked element was a link, with the buttonright class
alert('You clicked a button/link thingy');
}
}, false);
That's the cleanest way to do things (one event listener handles all click events). Of course, you can bind the handler to specific elements, too:
var buttons = document.querySelectorAll('.buttonright'),
handler = function(e)
{
alert('Clicked!');
};
for (var i=0;i<buttons.length;++i)
{
buttons[i].addEventListener('click',handler, false);
}
Depending on how you want to handle the event, there are numerous roads you can take.
The simplest one is this :
<script type="text/javascript">
function buttonRight_onclick(event, sender)
{
alert("HEY YOU CLICKED ME!");
}
</script>
<a class="buttonright" click="buttonRight_onclick(event, this)">
whereas if you were using a framework like jQuery, you could do it like this:
<script type="text/javascript">
$(document).ready(function() {
$(".buttonright").on("click", function(event) {
alert("HEY YOU CLICKED ME!");
});
});
</script>
<a class="buttonright" >Bump</a>
<a class="buttonright" >Also bump</a>
<script type="text/javascript">
function Button_onclick(event, sender)
{
alert("Button Clicked!");
}
</script>
<a class="Button" click="Button_onclick(event, this)">
When a user clicks a certain link I would like to present them with a confirmation dialog. If they click "Yes" I would like to continue the original navigation. One catch: my confirmation dialog is implemented by returning a jQuery.Deferred object which is resolved only when/if the user clicks the Yes button. So basically the confirmation dialog is asynchronous.
So basically I want something like this:
$('a.my-link').click(function(e) {
e.preventDefault(); e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
})
})
Of course I could set a flag and re-trigger click but that is messy as heck. Any natural way of doing this?
Below are the bits from the code that actually worked in Chrome 13, to my surprise.
function handler (evt ) {
var t = evt.target;
...
setTimeout( function() {
t.dispatchEvent( evt )
}, 1000);
return false;
}
This is not very cross-browser, and maybe will be fixed in future, because it feels like security risk, imho.
And i don't know what happens, if you cancel event propagation.
It could be risky but seems to work at the time of writing at least, we're using it in production.
This is ES6 and React, I have tested and found it working for the below browsers. One bonus is if there is an exception (had a couple during the way making this), it goes to the link like a normal <a> link, but it won't be SPA then ofc.
Desktop:
Chrome v.76.0.3809.132
Safari v.12.1.2
Firefox Quantum v.69.0.1
Edge 18
Edge 17
IE11
Mobile/Tablet:
Android v.8 Samsung Internet
Android v.8 Chrome
Android v.9 Chrome
iOs11.4 Safari
iOs12.1 Safari
.
import 'mdn-polyfills/MouseEvent'; // for IE11
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class ProductListLink extends Component {
constructor(props) {
super(props);
this.realClick = true;
this.onProductClick = this.onProductClick.bind(this);
}
onProductClick = (e) => {
const { target, nativeEvent } = e;
const clonedNativeEvent = new MouseEvent('click', nativeEvent);
if (!this.realClick) {
this.realClick = true;
return;
}
e.preventDefault();
e.stopPropagation();
// #todo what you want before the link is acted on here
this.realClick = false;
target.dispatchEvent(clonedNativeEvent);
};
render() {
<Link
onClick={(e => this.onProductClick(e))}
>
Lorem
</Link>
}
}
I solved problem by this way on one of my projects. This example works with some basic event handling like clicks etc. Handler for confirmation must be first handler bound.
// This example assumes clickFunction is first event handled.
//
// you have to preserve called function handler to ignore it
// when you continue calling.
//
// store it in object to preserve function reference
var ignoredHandler = {
fn: false
};
// function which will continues processing
var go = function(e, el){
// process href
var href = $(el).attr('href');
if (href) {
window.location = href;
}
// process events
var events = $(el).data('events');
for (prop in events) {
if (events.hasOwnProperty(prop)) {
var event = events[prop];
$.each(event, function(idx, handler){
// do not run for clickFunction
if (ignoredHandler.fn != handler.handler) {
handler.handler.call(el, e);
}
});
}
}
}
// click handler
var clickFunction = function(e){
e.preventDefault();
e.stopImmediatePropagation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(go.apply(this, e));
};
// preserve ignored handler
ignoredHandler.fn = clickFunction;
$('.confirmable').click(clickFunction);
// a little bit longer but it works :)
If I am understanding the problem correctly, I think you can just update the event to be the original event in that closure you have there. So just set e = e.originalEvent in the .done function.
https://jsfiddle.net/oyetxu54/
MyApp.confirm("confirmation?")
.done(function(){ e = e.originalEvent;})
here is a fiddle with a different example (keep the console open so you can see the messages):
this worked for me in chrome and firefox
I solved this by:
placing a event listener on a parent element
removing the class from the link ONLY when the user confirms
reclicking the link after I have removed the class.
function async() {
var dfd = $.Deferred();
// simulate async
setTimeout(function () {
if (confirm('Stackoverflow FTW')) {
dfd.resolve();
} else {
dfd.reject();
}
}, 0);
return dfd.promise();
};
$('.container').on('click', '.another-page', function (e) {
e.stopPropagation();
e.preventDefault();
async().done(function () {
$(e.currentTarget).removeClass('another-page').click();
});
});
$('body').on('click', function (e) {
alert('navigating somewhere else woot!')
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
Somewhere else
</div>
The reason I added the event listener to the parent and not the link itself is because the jQuery's on event will bind to the element until told otherwise. So even though the element does not have the class another-page it still has the event listener attached thus you have to take advantage of event delegation to solve this problem.
GOTCHAS this is very state based. i.e. if you need to ask the user EVERYTIME they click on a link you'll have to add a 2nd listener to readd the another-page class back on to the link. i.e.:
$('body').on('click', function (e) {
$(e.currentTarget).addClass('another-page');
});
side note you could also remove the event listener on container if the user accepts, if you do this make sure you use namespace events because there might be other listeners on container you might inadvertently remove. see https://api.jquery.com/event.namespace/ for more details.
We have a similar requirement in our project and this works for me. Tested in chrome and IE11.
$('a.my-link').click(function(e) {
e.preventDefault();
if (do_something === true) {
e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
do_something = false;
// this allows user to navigate
$(e.target).click();
})
}
})
I edited your code. New features that I added:
Added namespace to event;
After click on element event will be removed by namespace;
Finally, after finish needed actions in "MyApp" section continue propagation by triggering others element "click" events.
Code:
$('a.my-link').on("click.myEvent", function(e) {
var $that = $(this);
$that.off("click.myEvent");
e.preventDefault();
e.stopImmediatePropagation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
$that.trigger("click");
});
});
This is untested but might serve as a workaround for you
$('a.my-link').click(function(e) {
e.preventDefault(); e.stopPropogation();
MyApp.confirm("Are you sure you want to navigate away?")
.done(function() {
//continue propogation of e
$(this).unbind('click').click()
})
})