jQuery HTML click event firing twice - javascript

I have a custom select menu button, and I have bound to the html click event to close it. But the event is firing twice.
http://jsfiddle.net/GnzBj/1/
$(function () {
$('html').click(function () {
console.log('html');
});
});
Any one know why/how to prevent it firing twice?

The reason why the event is triggered twice is because you have the whole UI inside a label.
<div ...>
<label for="xmod-form-51183d51afa3d" ... >
<select name="theme" id="xmod-form-51183d51afa3d" ...>
...
</select>
...
</label>
</div>
Clicking on the label will also trigger a click event on the form element it relates to.
If you remove the label element, it works as expected: http://jsfiddle.net/GnzBj/5/.
In case you need the label, let it contain as few as possible of the UI, but clicking on it will still trigger two events.

Related

Why is trigger() triggering the select() handler 3 times?

$("#select-test").select(function(e){
//console.log(e)
console.log("selected")
})
$("#newTest").click(()=> {
$("#select-test").trigger("select")
})
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<button id="newTest">TEST ME</button>
<input type="text", id="select-test" value="some">
This is in Chrome, in Firefox, it's triggered twice.
I read this JQuery - Why does Trigger method call it three times? thread which talked about this issue, but honestly I couldn't understand it.
Somebody said:
we have 1 isTrigger and 2 simple select events.
What is that mean? 2 simple select events? Where? We only have 1 select event, where's the second one?
The best answer says that this happens because of bubbling, but... how? I mean, where's the bubbling? I don't see how this explain the event handler being triggered 3 times. Bubbling is when you target a child element, and the parent with the same handler is triggered too, but that's not what we have here. We only have ONE select handler, so .. where's the bubbling? And why is it triggered twice in Firefox?
A select needs a prevent default if you wish to stop it at some point.
the select event isn't the focus event, when a field becomes in focus. The select event gets fired when something is selected.
When you trigger the select event with the below code and have the browser tools open, you will see that the debugger shows the below code. When you select call stack -> dispatch, you can see what triggered the event and see that the third event is triggered by a native event.
var count = 0;
$("#select-test").select(function(e){
if(count == 2) {
debugger;
}
count++;
console.log("selected")
})
$("#newTest").click(()=> {
count = 0
$("#select-test").trigger("select")
})
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<button id="newTest">TEST ME</button>
<input type="text", id="select-test" value="some">
If we look at the documentation for select we see this:
In addition, the default select action on the field will be fired, so the entire text field will be selected.
So what happens is:
You trigger select -> jquery fires select to all event handlers
jQuery focusses the field, which triggers an automatic browser select all text, which triggers the select event.
Jquery selects all text in the field, native select is triggered, which is wrapped by jquery and then passed on to the handlers.
To stop any of these steps from happening you can use event.preventDefault() on the first time select is triggered, this will have the side event of not selecting the text.
Rather than using select I suggest you use focus() unless you really need to know what text is selected every time a select is triggered.
I have no idea why .select() (and also .on("select")) trigger three times, but .one("select") triggers once :
const $selectTest = $("#select-test");
$selectTest.one("select", function(e){
console.log(e.target)
console.log("selected")
});
$("#newTest").click(()=> {
console.log("Clicked");
$selectTest.trigger("select")
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="newTest">TEST ME</button>
<input type="text", id="select-test" value="some">

Angular2 toggle/open ng2-select dropdown

Angular 4.3+
I am using ng2-select dropdown.
I want the drop down to be opened on some event. I want to trigger the event that would open the drop down.
Dropdown:
<ng-select
[items]="list"
[(ngModel)]="modelName"
placeholder="Select">
</ng-select>
When the user clicks on the dropdown it gets focus and the list is being displayed. I would like this happen on some click event on the different button.
What I have tried so far:
this.el is ElementRef to the ng-select element. (Getting the element reference for ng-select correctly, it console log correct element that I am trying to trigger events.)
As it opens when the user clicks on it, I tried to trigger a click event on an element.
this.el.nativeElement.click();
Tried to trigger click event on .ui-select-toggle element.
this.el.nativeElement.querySelector('.ui-select-toggle').dispatchEvent(new Event('click'));
Similarly I tried to trigger focus event but didn't work.
Is there any other way that we can trigger some event on ng-select element that gets focus on ng-select and get the drop down open.
I wrap up the click event trigger code inside the setTimeOut function like below and it is working fine now.
setTimeout(() => {
this.el
.nativeElement
.querySelector('.ui-select-toggle')
.dispatchEvent(new Event('click'));
});
.ui-select-toggle is the element does dropdown toggle on click, so I figured if I trigger click event on that element it will toggle/open the drop-down.
So I tried the following(without the setTimeOut wrapper)
this.el
.nativeElement
.querySelector('.ui-select-toggle')
.dispatchEvent(new Event('click'));
And it did nothing. But when I wrap that code in setTimeOut it actually triggers the click event and I get expected functionality.
Here I am not sure why it functions like that but I believe this is something related to javascript.
If you are using multiple mode:
openNg2Select(ng2Select: SelectComponent) {
setTimeout(() => {
const ng2 = ng2Select.element.nativeElement.querySelector('.ui-select-search');
ng2.dispatchEvent(new Event('click'));
});
}
In HTML:
<ng-select #ng2Select [multiple]="true" [items]="items" (selected)="selected($event)"
(removed)="removed($event)" placeholder="Please Select"
[(ngModel)]="selectedItem">
</ng-select>
<button class="btn btn-pripary" (click)="openNg2Select(ng2Select)>Open ng2-select</button>

Stop a dynamic bound eventhandler to fire immediately

I'm using jQuery 2.1.3. I try to build a button that will show/hide content on click. I also want to hide the content when there's a click on a wrapping container (in my application it's the document itself, but I used a regular div in my example below). As there is some more logic to handle I'm also making use of custom events that I fire via .trigger().
This works fine but I have a problem. When I show the hidden content I apply an eventlistener to document to hide the content when the user clicks anywhere on it. This eventhandler is fired straight after the content is shown resulting in my content gets hidden immediately.
I can only get around this by using $evt.stopPropagation() in my toggle buttons event handler. But that's no solution for me as I need the event to bubble up as other elements listen for that.
Heres the simplified HTML:
<div id="document">
<button id="btn" type="button">Toggle</button>
<div id="content" class="hidden">Hidden Content</div>
</div>
And heres the JS to this:
//trigger custom show event
var show = function () {
$("#document").trigger("show");
};
//trigger custom hide event
var hide = function () {
$("#document").trigger("hide");
};
//handle custom show event
$("#document").on("show", function () {
$("#content").removeClass("hidden");
$("#document").on("click", hide);//add listener to container, fires immediately
});
//handle custom hide event
$("#document").on("hide", function () {
$("#document").off("click", hide);//remove listener from container
$("#content").addClass("hidden");
});
//on toggle button click call custom events
$("#btn").click(function ($evt) {
//$evt.stopPropagation(); would work but event should propagate
$("#content").hasClass("hidden") ? show() : hide();
});
JSFiddle
What could I do about this? Stopping event propagation on toggle button click would show my content but i need this event to bubble up as there are other elements around waiting for that. How could I avoid that the applied event listener on document fires immediately?
PS: jQuerys .show()/.hide() functions are no alternative for me.

Why does html label for trigger callback on containing div twice?

I saw another question that asks something similar, but the difference is that they put the input element inside the label tag, whereas mine is outside:
<!DOCTYPE html>
<head>
<style>
div {
background-color: #AAA;
width: 100px;
}
</style>
</head>
<body>
<div id='foo' onClick="alert('clicked')">
<label for='foo-pie'>Pick Foo:</label>
<select id='foo-pie' name='wat[wat]'>
<option value='pie'>Foo</option>
</select>
</div>
</body>
</html>
If I click on the div, not on the label, the alert triggers once. If I click on the label, the alert triggers twice. Why is this? It seems to me like the alert should trigger once regardless. If I remove the for= it only triggers once, which suggests to me that there's some sort of event bubbling going on... JSFiddle
This is because label is an interactive element. When clicked it triggers an activation event on its associated element. This activation event usually causes a click event, so you get two clicks instead of one. One for the actual user click and one caused by the activation event.
http://www.w3.org/TR/html5/forms.html#the-label-element
For example, on platforms where clicking a checkbox label checks the
checkbox, clicking the label in the following snippet could trigger
the user agent to run synthetic click activation steps on the input
element, as if the element itself had been triggered by the user:
<label><input type=checkbox name=lost> Lost</label>
On other
platforms, the behavior might be just to focus the control, or do
nothing.
While the example from the docs shows an onClick for the input element, the event would bubble up so in your case your div gets the bubbled click event
Interactive-content doc
To prevent this you can prevent the default action, or use stopPropagation to stop the event from bubbling up.
Inline
<label onclick="return false;" for='foo-pie'>Pick Foo:</label>
in code
HTML
<label id="foolabel" for='foo-pie'>Pick Foo:</label>
JS
document.getElementById("foolabel").addEventListener(function(e){
e.preventDefault(); //Or
e.stopPropagation();
});
Example
<div id='foo' onClick="alert('clicked')">
<label onclick="event.stopPropagation();" for='foo-pie'>Pick Foo:</label>
<select id='foo-pie' name='wat[wat]'>
<option value='pie'>Foo</option>
</select>
</div>
My theory is that this is a feature called event propagation, and by defining your <div> with the onclick attribute it has somehow bound that event to the children as well.
Try throwing e.stopPropagation into your event callback and it should stop it from being called multiple times.
MDN Example

How to select check box without selecting the whole row (see body)?

Say, I have an html:
<div class="row">
<input type="checkbox">
</div>
When user clicks on the whole row, it becomes highlighted (selected class added by an onClick event). I attach onClick event to elements with class .row.
When user clicks on checkbox (which is inside .row), this checkbox becomes selected. But row should not be highlighted.
Is it possible to exclude the area of the checkbox from the area of the .row for an onClick event?
UPDATE
Here is what I have now: http://jsfiddle.net/saAGU/
I don't want class to be toggled when I click exactly on checkbox.
UPDATE 2
Here is the working solution with jQuery for future use: http://jsfiddle.net/saAGU/2/
Yes, it's possible. The right way to do it is putting a onclick event on the checkbox, capture the event and stop its propagation.
Something like this:
function checkboxClick (e) {
e.stopPropagation();
}
Please tell me if it worked
add click event to every checkbox and stop its propogation .this should work -
e.stopPropagation in event handler for checkbox click
http://jsfiddle.net/saAGU/3/
If you capture the event, and look at the event.toElement, you can see if they clicked on the .row or something else.
http://jsfiddle.net/saAGU/1/
$('.row').click(function(event){
if (event.toElement !== this) // Did the user click on the row directly?
return;
$(this).toggleClass('selected');
});

Categories