I want to trigger click event on a element when mousedown occurs. Also, I want to enable this feature for all elements in a html page.
Is it possible with jQuery on Chrome ?
Here's my first attempt;
$.fn.mousedown = function (onclick) {
this.bind("click", function (e) { onclick.call(this, e); });
return this;
};
But this mousedown elements fired after click occurs.
$(document).on('mousedown', function (e) { $(e.target).trigger('click') })
I'm not sure though for what this should be useful.
To prevent the second click (which is the normal click) you have to do some extra work
$(document).on('mousedown', function (e) {
$(e.target).trigger('click').once('click', function (e) {
e.preventDefault();
e.stopPropagation();
});
})
Related
I have 2 seperate event listeners, the first one is a click event, second one is a window beforeunload listener:
This is the click event listener:
document.body.addEventListener('click',function(event){
if (event.defaultPrevented) return;
console.log('click EL');
})
and this is the beforeunload listener:
window.addEventListener("beforeunload", function (e) {
e.preventDefault();
console.log('beforeunload EL');
})
Can I stop the click event from firing if the beforeunload is fired? I've tried doing it with event.preventDefault(); still it didn't work.
Try this, This is working code:
var beforeunload = function (e) {
e.preventDefault();
console.log('beforeunload EL');
}
window.addEventListener("beforeunload", beforeunload);
document.body.addEventListener('click',function(event){
if (event.defaultPrevented) return;
console.log('click EL');
window.removeEventListener("beforeunload", beforeunload);
});
Just remove the listener if your click happened
Since dispatchEvent, as per the docs, will apply the:
normal event processing rules (including the capturing and optional
bubbling phase)
I'm looking for something similar but with a way to skip this process and trigger the event directly on the element. To trigger the default element event behavior while bypassing the processing stage.
As in, to capture the event at window level (before it reaches the other capture triggers) and pass it straight to the component (text area) invoking it directly.
(For example to trigger the default keydown of a text area without going through the hierarchy)
I've been trying to do it like this but if there is another event at window level this will not work:
window.addEventListener("keydown", this.keyDown, true);
keyDown = (event) => {
event.preventDefault();
event.nativeEvent && event.nativeEvent.stopImmediatePropagation();
event.stopImmediatePropagation && event.stopImmediatePropagation();
event.stopPropagation();
// Pass event straight to the element
return false;
};
I'm looking to trigger the default element event behavior while bypassing the processing
There may well be a more elegant way to do this, but one option is to remove the element from the DOM first, dispatch the event to it, then put it back into the DOM:
document.body.addEventListener('keydown', () => {
console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
console.log('body keydown bubbling');
});
const input = document.querySelector('input');
input.addEventListener('keydown', () => {
console.log('input keydown');
});
const node = document.createTextNode('');
input.replaceWith(node);
input.dispatchEvent(new Event('keydown'));
node.replaceWith(input);
<input>
Since the element isn't in the DOM when the event is dispatched, the elements which used to be its ancestors won't see the event.
Note that events dispatched to detached elements do not capture/bubble regardless, not even to parents or children of element the event was dispatched to.
Without removing the element from the DOM entirely beforehand, if the input can exist in a shadow DOM, you can also dispatch an event to it there, and the event won't capture down or bubble up (though user input, not being custom events, will propagate through):
document.body.addEventListener('keydown', () => {
console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
console.log('body keydown bubbling');
});
outer.attachShadow({mode: 'open'});
const input = document.createElement('input');
outer.shadowRoot.append(input);
input.addEventListener('keydown', () => {
console.log('input keydown');
});
input.dispatchEvent(new Event('keydown'));
<div id="outer"></div>
Another approach would be to call stopPropagation and stopImmediatePropagation in the capturing phase, at the very beginning, when the event is at the window, and then manually call the listener function you want. Make sure to attach your window listener before any other scripts on the page run, to make sure none of the page's listeners can see the event first:
// Your script:
const input = document.body.appendChild(document.createElement('input'));
input.className = 'custom-extension-element';
const handler = (e) => {
setTimeout(() => {
console.log(e.target.value);
});
};
window.addEventListener(
'keydown',
(e) => {
if (e.target.closest('.custom-extension-element')) {
e.stopImmediatePropagation(); // Stop other capturing listeners on window from seeing event
e.stopPropagation(); // Stop all other listeners
handler(e);
}
},
true // add listener in capturing phase
);
// Example page script
// which tries to attach listener to window in capturing phase:
window.addEventListener(
'keydown',
(e) => {
console.log('page sees keydown');
},
true
);
document.body.addEventListener('keydown', () => {
console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
console.log('body keydown bubbling');
});
The best way around propagation issues is to just execute the function:
function tester(){
console.log("Just fire the function! Don't dispatchEvent!");
}
tester();
document.getElementById('test').onkeyup = function(e){
e.stopPropagation();
console.log(this.id); console.log(e.target); tester();
}
document.body.onkeyup = ()=>{
console.log("This shouldn't fire when on #test");
}
<input id='test' type='text' value='' />
Finding myself in a bit of a strange position where I have to reference the tooltip within an instantiation for all tooltips.
$('body').tooltip({
selector: '[data-toggle="tooltip"]',
html: true,
animation: false,
}).on("mouseenter", function (e) {
e.stopPropagation();
var _this = e.target;
$(_this).tooltip("show");
$(".tooltip").on("mouseleave", function () {
$(_this).tooltip('hide');
});
}).on("mouseleave", function (e) {
e.stopPropagation();
var _this = e.target;
setTimeout(function () {
if (!$(".tooltip:hover").length) {
$(_this).tooltip("hide");
}
}, 300);
});
That being said, how can I:
Reference the actual element that is triggering this jQuery call
Keep the tooltip open while either the actual tooltip or element that generated it are being hovered over?
Here is a link to a JSFiddle prototype:
https://jsfiddle.net/cwv57weu/8/
Within your '.on()' call, you can add an 'event' argument to your anonymous function. This will contain all of the data from the event including the element which triggered the event (it will be referenced as 'target').
}).on("mouseenter", function (event) {
$(event.target).tooltip("show");
})
the event argument contains a ton of data, I would play around with it by console.log(event) within your anonymous function to get a feel as to what data is available to you.
Use event.target.
$('body').tooltip({
selector: '[data-toggle="tooltip"]',
html: true,
animation: false,
}).on("mouseenter", function (e) {
var _this = e.target;
$(_this).tooltip("show");
$(".tooltip").one("mouseleave", function () {
$(_this).tooltip('hide');
});
}).on("mouseleave", function (e) {
var _this = e.target;
setTimeout(function () {
if (!$(".tooltip:hover").length) {
$(_this).tooltip("hide");
}
}, 300);
});
e.target is the actual element that the event originated on, while this is the element that the event listener was attached to (equivalent to e.currentTarget).
Note that because of event bubbling, the event will fire on all the containing elements up to body. You may want to use e.stopPropagation() to prevent bubbling, so you only process the deepest element.
I also changed the mouseleave handler on .tooltip to use .one(). Otherwise, every time you enter something, you'll add another mouseleave handler to all the tooltips, without removing the previous one, and soon there will be thousands of handlers running (this is why it's generally wrong to bind event handlers inside other event handlers). I'm not really sure you need both that mouseleave handler and the one you attach to body.
I have a bit of a head scratcher when it comes to using stopPropagation in javascript. According to many different sources stopPropagation should stop bubbling of the event to parent elements, however, when I use it it seems to stop the event from being called after the first click. I have worked up a very simple bit of code to reproduce the behaviour below:
HTML:
<div id="root">
<div id="top">
<h1>Click Me!</h1>
</div>
</div>
js/jQuery:
var myEvent = document.createEvent("Event");
myEvent.initEvent("boop", true, true);
$("#root").on('boop', function (e) {
alert("root boop!");
});
$("#top").on('boop', function (e) {
// After this is called, this event handler will never fire again.
e.stopPropagation();
alert("top boop!");
});
$("h1").click(function (e) {
$("#top").get(0).dispatchEvent(myEvent);
// I know that $("#top").trigger will prevent the problem, what is wrong with the form above?
});
There is a Fiddle as well.
You dispatch myEvent on which you eventually call .stopPropagation(). Every click thereafter use the same instance of myEvent on which the propagation has been stopped.
You'll need to make a copy of the event before dispatching it if you want to be able to click multiple times.
...or you could rewrite your JavaScript like this:
$("#root").on('boop', function (e) {
alert("root boop!");
});
$("#top").on('boop', function (e) {
e.stopPropagation();
alert("top boop!");
});
$("h1").click(function (e) {
var myEvent = document.createEvent("Event");
myEvent.initEvent("boop", true, true);
$("#top").get(0).dispatchEvent(myEvent);
});
Working JS Fiddle
In my html I have a span of class dragHandle embedded within a li.
<div class='treeView'>
<ul class='tree'>
<li><span class="dragHandle"></span>Item 1
<ul>
<li><span class="dragHandle"></span>Item 2 link</li>
</ul>
</li>
</ul>
I attach event handlers using jQuery as follows:
$(".tree li").click(function(event) {
alert("click");
event.stopPropagation();
});
$(".dragHandle").mousedown(function(event) {
alert("down");
event.stopPropagation();
});
$(".dragHandle").mouseup(function(event) {
alert("Up");
event.stopPropagation();
});
When I mousedown and mouse up over the element I get the down and up alerts, however I also get the click alert of the li's event handler too. I thought that this should be prevented from by the call to event.stopPropagation in the mousedown and mouseup handlers. How do I stop the click event being called for mousedown/up events on the dragHandle?
TIA,
Adam
How do I stop the click event being called for mousedown/up events on the dragHandle?
You capture... and eat... that event:
$(".dragHandle").click(function(event) { event.stopPropagation(); });
The key here is that click, mousedown, and mouseup are distinct events. Although you might think of a click as being a mousedown followed by a mouseup, in reality you might have click events triggered by user actions that don't even involve the mouse, as well as combinations of mousedown and mouseup that don't result in any click events at all.
You could create a simple wrapper-"class", that keeps track of mouse-down and up events:
(function () {
var DragDropHandler = {
isDrag: false,
mouseDownHandler: function (event) {
alert("down");
event.stopPropagation();
this.isDrag = true;
},
mouseUpHandler: function (event) {
alert("Up");
event.stopPropagation();
this.isDrag = false;
},
clickHandler: function (event) {
event.stopPropagation();
// Check if we've already received a mouseDown-event. If we have,
// disregard the click event since we want drag-functionality
if(this.isDrag) { return; }
alert("click");
}
};
$(".tree li").click(function(event) {
DragDropHandler.clickHandler.call(DragDropHandler, event);
});
$(".dragHandle").mousedown(function(event) {
DragDropHandler.mouseDownHandler.call(DragDropHandler, event);
});
$(".dragHandle").mouseup(function(event) {
DragDropHandler.mouseUpHandler.call(DragDropHandler, event);
});
})();
This creates a closure and delegates the event handling to the DragDropHandler-object. Note that I've used function.call (the first parameter is the context) to ensure that this refers to the DragDropHandler-object inside its methods. Since we have created an anonymous function that can not be reached from global space, I think it's acceptable to use the DragDropHandler reference inside the wrapper event handlers.