Click event not emitted when mousedown/mouseup do add/removeClass on element - javascript

I'm attaching mousedown, mouseup and click handlers to an element. On mousedown I add a class to the element, on mouseup I remove the class, and on click I do some work. (This is a simplification of the context. In my project the click event is handled by a 3rd party component.)
The problem I'm having is that the click event is never emitted in Safari and Firefox, but it works just fine in Chrome. (I don't know what IE does. I don't have access to it, and don't care about it.)
The code is as follows:
HTML:
<div id="clickme">
<div class="normal"></div>
<div class="highlight"></div>
</div>
<input type="text" id="textinput"/>
CSS:
#clickme:not(.active) > .highlight {
display: none;
}
#clickme.active > .normal {
display: none;
}
.normal, .highlight {
width: 100px;
height: 100px;
}
.normal {
background: blue;
}
.highlight {
background: red;
}
JS:
var clickme = $('#clickme');
var textinput = $('#textinput');
clickme.on('mousedown', function(e) {
clickme.addClass('active');
// ^-- comment this out and the click event starts working
});
clickme.on('mouseup', function(e) {
clickme.removeClass('active');
// ^-- comment this out and the click event starts working after the second click
});
clickme.on('click', function(e) {
e.preventDefault();
textinput.val(Date.now());
});
JSFiddle: https://jsfiddle.net/xLskk3po/14/
JSFiddle without JQuery: https://jsfiddle.net/xLskk3po/15/ It shows that it's not a JQuery problem.

I stumbled upon this SO question: When a mousedown and mouseup event don't equal a click and it looks like my issue is similar to that. So I did something silly: I put a transparent, absolutely positioned element on top.
HTML:
<div id="clickme">
<div class="normal"></div>
<div class="highlight"></div>
<div class="abs"></div> <!-- this is the absolute element, covering #clickme -->
</div>
<input type="text" id="textinput"/>
That fixed it.

Related

On element click Trigger event fire twice for input checkbox

when i click on the div element should trigger click on checkbox only once but for some reason i get event fired twice , i saw other topics with similar problem but noone helped me
$('div').click(function(e) {
$('input').trigger('click');
check();
});
function check() {
if ($('input').is(':checked')) {
console.log('input cheked')
} else {
console.log('unchecked')
}
}
.test {
height: 150px;
width: 150px;
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test">
<input type="checkbox" name="">
</div>
The issue is because the click occurs on the div, which triggers a click on the child checkbox which in turn propagates up the DOM and runs the click handler on the div again.
If you are trying to create a bigger hit-area for the checkbox, just use a label element instead. Then you get this behaviour for free without needing any JS.
If you want to know the state of a checkbox when it's changed, hook a change event handler to it. Try this:
$(':checkbox').on('change', function() {
if (this.checked) {
console.log('input checked')
} else {
console.log('unchecked')
}
});
.test {
height: 150px;
width: 150px;
background: red;
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label class="test">
<input type="checkbox" name="">
</label>
That is because, when you are trigerring click on the input the event is being bubbles to all its parents. To stop that use e.stopPropagation on the click event handler of input.
$('input').click(function(e) {
e.stopPropagation();
});
Read more about bubbling and capturing here.
Check the working code below:
$('div').click(function(e) {
$('input').trigger('click');
check();
});
$('input').click(function(e) {
e.stopPropagation();
});
function check() {
if ($('input').is(':checked')) {
console.log('input cheked')
} else {
console.log('unchecked')
}
}
.test {
height: 150px;
width: 150px;
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test">
<input type="checkbox" name="">
</div>
Your checkbox is inside div and you are binding click evet to div and from that you are triggering checkbox click event which again triggers click of div. That's why it's triggering 2 times.
You can directly go for checkbox change event:
$(':checkbox').on('change', function() {
if (this.checked) {
console.log('input checked')
} else {
console.log('unchecked')
}
});
.test{
height: 150px;
width: 150px;
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test">
<input type="checkbox" name="">
</div>
JQuery uses event bubbling when setting up events. This means that when you click the input the event is fired once for the input and then it 'bubbles' up the DOM tree to the parent DIV. This then notices the click event on the DIV and fires again. Therefore the event fires twice, once for the input and again for the DIV.
To stop this you will need to use the 'capture' technique instead of event bubbling. This would mean that you would use addEventListener and pass in the option as the third argument as true.
See here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
Also see here to understand bubbling vs capturing: https://javascript.info/bubbling-and-capturing

How to make sure clickable objects don't propagate to the wrong element?

Languages involved: HTML, CSS, JS
Context: I'm relatively new to web development. I have two elements overlapping each other. One is a slider, one is a div. The slider is on top of the div.
Code snippets:
<div id="myDiv">
<input id="mySlider" type="range" min=1 max=100 step=1>
</div>
and
initListeners() {
document.getElementById("myDiv").addEventListener("click", divFunction);
document.getElementById("mySlider").addEventListener("input", sliderFunction);
}
I need to make it that when you click the slider, it doesn't click the div. How would I go about doing that? I've tried z-index, but that doesn't seem to change anything.
Thanks in advance!
As I'm sure you've figured out by now, events in JavaScript by default bubble up from a child to a parent. You need to stop that from happening at the child level, also known as preventing propagation.
Using the stopPropagation function, you can handle this as follows:
function sliderFunction(e) {
e.stopPropagation();
}
Simple. That event will no longer reach the parent.
EDIT
While stop propagation is the correct method to use, event listeners must also match in type. Therefore, both the slider and the parent DIV must have click event listeners (instead of input and click). stopPropagation stops propagation of a specific type of event.
function divFunction() {
console.log('DIV clicked!');
}
function sliderFunction(event) {
event.stopPropagation();
console.log('Slider clicked!');
}
function initListeners() {
document.getElementById('myDiv').addEventListener('click', divFunction);
document.getElementById('mySlider').addEventListener('click', sliderFunction);
}
initListeners();
/* unnecessary visual aides */
body *:not(label) {
padding: 2rem;
outline: 1px solid red;
position: relative;
}
label {
display: inline-block;
position: absolute;
background: #222;
color: #fff;
top: 0; left: 0;
}
<div id="myDiv">
<label>#myDiv</label>
<div id="tools">
<label>#tools</label>
<input type="range" id="mySlider">
</div>
</div>
You can also check the target once you fire that click event. I've used this approach before:
JSFiddle: http://jsfiddle.net/L4ck7ygo/1/
function divFunction(e) {
if (e.target !== this) {
return;
} else {
console.log('hit');
}
}
When the fiddle first loads, click the slider and you'll see the console log out some text. To see it work, remove the line that is being pointed to and rerun the fiddle. Now when you click the slider, you won't see anything logged in the console, but if you click on the div and not the slider, it will log to the console.
function initListeners() {
document.getElementById("myDiv").addEventListener("click", divFunction);
document.getElementById("mySlider").addEventListener("input", sliderFunction);
}
initListeners();
function divFunction(e) {
console.log('Firing...') // <-- This will log on any click
if (e.target !== this) {
return;
} else {
console.log('hit'); // <-- This will NOT log except for div click
}
}
function sliderFunction() {
console.log('Doing stuffs...');
}
<div id="myDiv">
<input id="mySlider" type="range" min=1 max=100 step=1>
</div>
UPDATE: Stupidity on my part. I had the ordering wrong for the elements which caused propagation to not act as intended.

How to change image side of cursor on drag event using JQuery

When you drag an file on browser screen, an image appear side of mouse cursor that is windows default image. This images is various like Copy, Move and Forbide. See its at bottom.
How can i change image side of mouse cursor to this images using javascript or JQuery? For example when i drag a file and move mouse in undragable area, forbiden image display side of cursor.
You can use the dataTransfer.dropEffect property of the dragover event to set the small image besides the cursor:
$(".targetDiv").on("dragover", function (event) {
event.preventDefault();
event.originalEvent.dataTransfer.dropEffect = "none"; // Shows the "forbidden" image
});
The values for that property are copy, move, link and none. You can test these values in the code snippet below. Please note that the originalEvent must be used. According to my tests, it works in Firefox and Chrome but not in IE.
$(function () {
$(".targetDiv").on("dragover", function (event) {
event.preventDefault();
event.originalEvent.dataTransfer.dropEffect = event.target.getAttribute("data-effect");
});
});
.targetDiv
{
display: inline-block;
border: solid 1px black;
width: 80px;
height: 50px;
margin: 4px;
text-align: center;
line-height: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Drag a file over each block </p>
<div>
<div data-effect="link" class="targetDiv">Link</div>
<div data-effect="move" class="targetDiv">Move</div>
</div>
<div>
<div data-effect="copy" class="targetDiv">Copy</div>
<div data-effect="none" class="targetDiv">None</div>
</div>
You can change the cursor image by changing the property of the cursor by css using jquery.
function ondrag(event) {
$('body').css('cursor', 'wait');
}
You can check the various cursor property here.
http://www.w3schools.com/cssref/pr_class_cursor.asp
If you want to replace the cursor with a custom image you can use this:
https://github.com/Webbrother/jquery.change-cursor
If you want to limit draggables to a certain area,
Try using "containment" option:
http://docs.jquery.com/UI/Draggable#option-containment
You can do it with jquery draggable
Here is the preview what i have done
$( ".your_image" ).draggable({
drag: function() {
$(".your_image").css("cursor","url(https://cdn1.iconfinder.com/data/icons/CrystalClear/16x16/actions/move.png), auto");
},
stop: function() {
$(".your_image").css("cursor","url(https://cdn4.iconfinder.com/data/icons/32x32-free-design-icons/32/Copy.png), auto");
}
});
.your_image{
height:100px;
width:100px;
background-color:red;
cursor:url(https://cdn4.iconfinder.com/data/icons/32x32-free-design-icons/32/Copy.png), auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div>
<div class="your_image">
</div>
</div>
<div class="log">
</div>

Javascript pass onclick on child to parent

I have div (.upload-drop-zone, yellow zone at screenshot) with another div (.dropzone-width, blue zone) inside.
<div class="upload-drop-zone dz-clickable" id="drop-zone-600">
<div class="dropzone-width">600 PX</div>
</div>
There is a javascript onclick event attached to .upload-drop-zone (when I click on it, it shows file chooser dialog). Event attached by third-party plugin, so I have no access to function which be called.
The problem is that if I make click on .dropzone-width, click event did not pass to .upload-drop-zone so nothing happens instead of showing file chooser dialog. What can I do to fix it?
P.S.: Sorry for bad english.
Try this, I had a same issue before. No javscript required...
.dropzone-width { pointer-events: none; }
You can listen for a click in the inner div and fire the click on the outer div.
$("#drop-zone-600").click(function (e) {
alert("hey");
});
$("#dzw").click(function (e) {
$("#drop-zone-600").onclick();
});
.upload-drop-zone {
width: 200px;
height: 200px;
border: 1px solid red;
background: darkred;
}
.dropzone-width {
width: 100px;
height: 100px;
border: 1px solid green;
background: lightgreen;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="upload-drop-zone dz-clickable" id="drop-zone-600">
<div id ="dzw" class="dropzone-width">600 PX</div>
</div>
Despite the fact that the alert function is inside the click event listener of the #drop-zone-600 div, you can see the alert by clicking any of the divs.
One possibility is to synthetically fire the click event. See How can I trigger a JavaScript event click
fireEvent( document.getElementById('drop-zone-600'), 'click' );
Try this via jquery:
$(".dropzone-width").on("click", function(){
$("#drop-zone-600").trigger("click");
});

Blur issues on drop-down search box in jQuery

I've implemented a search box that acts as a filter. When a user clicks in the search area, a drop-down box is displayed showing all of the possible options. Typing in the search box filters the results. Clicking outside of the box hides the results.
It uses the following HTML/CSS heiarchy
&ltdiv class="search">
&ltinput type="text" name="search" value="search" placeholder="search"/>
&ltdiv class="results">
&ltdiv class="result">
Result 1
&lt/div>
&ltdiv class="result">
Result 2
&lt/div>
...
&lt/div>
&lt/div>
I use jQuery to show/hide the dropdown on focus/blur events
var searchBar = {
init : function(){
$('input[name=search]').focus(searchBar.openSearch);
$('input[name=search]').blur(searchBar.closeSearch);
$('div.result').each(function(e){
$(this).click(draftboardDynamic.selectResult);
});
},
openSearch : function(e){
$('div.results').show();
},
closeSearch : function(e){
$('div.results').hide();
},
selectResult : function(e){
console.log($(this));
},
}
$(document).ready(searchBar.init);
This works quite well and I can open, close, and search (JS removed for clarity) without issue. The only bit I'm having trouble with is selecting results. The blur event seems to trigger before result.click events and the handler is never called. I can correct this issue by removing the blur binding, however, I can't figure out how to close my drop-down box when the input loses focus.
Any ideas?
This is a tough one because the .blur event will always fire before the .click. There are two possible solutions, neither of which is particularly desirable:
Unbind the .blur event when hovering over div.result. Rebind it on mouseout.
Instead of doing this with .blur, bind a click event to the document and check that the target is not one of the search components.
Use "mousedown" event instead of "click":
$(".container")
.on("blur focus", "input", function({type}) {
$(this).next().toggle(type === "focusin");
})
.on("mousedown", "ul", function({target: {innerText}}) {
$(this).prev().val(innerText);
});
$(".container")
.on("blur focus", "input", function({type}) {
$(this).next().toggle(type === "focusin");
})
.on("mousedown", "ul", function({target: {innerText}}) {
$(this).prev().val(innerText);
});
.container {
position: relative;
display: inline-block;
}
input, ul {
border: solid 1px black;
}
ul {
list-style: none;
position: absolute;
left: 0;
right: 0;
z-index: -1;
margin: -1px 0 0;
padding: 0;
}
label, li {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="_input"><b>Select value:</b></label>
<div class="container">
<input id="_input">
<ul style="display:none">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
Picked a wrong value?
Binding and unbinding the blur event on mouse over / mouse out works. For those interested i've created a fiddle to demonstrate this: https://jsfiddle.net/AdamKMorris/uoqvfy2L/
My example uses basic jquery and .bind/.unbind to toggle the search box:
$("#searchButton").click(function()
{
$("#searchBar").slideToggle();
$("#searchText").focus();
}).mouseover(function()
{
$("#searchText").unbind('blur');
}).mouseout(function()
{
$("#searchText").bind('blur', function()
{
$("#searchBar").slideToggle("fast");
});
});

Categories