How to send mouseover event to parent div? - javascript

I have a Div in which there is a text input, like this:
<div id="parentDive" class="parent">
<input id="textbox"></input>
</div>
I have assigned a functionality to the Div mouseover event and mouseout event by means of JQuery, but when I move my mouse over the text input, it calls mouseout event while it's in the DIV.
How to solve this problem? Should I send the event to the parent? How?

Use the jQuery .hover() method instead of binding mouseover and mouseout:
$("#parentDive").hover(function() {
//mouse over parent div
}, function() {
//mouse out of parent div
});
$("#textbox").hover(function() {
//mouse over textbox
}, function() {
//mouse out of textbox
});
Live test case.
The .hover() is actually binding the mouseenter and mouseleave events, which are what you were looking for.

I suggest to you to use .hover() not .mouseover() and .mouseout() here is a live working example
http://jsfiddle.net/DeUQY/
$('.parent').hover(function(){
alert('mouseenter');
},function(){
alert('mouseleave');
}
);

You need to use a few steps to make that work.
First, create the parent hover functions which would be enter() and exit(). These are setup using the hover() function. Then create the children enterChild() and exitChild() function. The children just change a flag that allows you to know whether a child is being hovered and thus the parent is still being considered to be hovered.
Whatever you want to do in the exit() function, you cannot do it immediately because the events arrive in the correct order for a GUI, but the wrong order for this specific case:
enter parent
exit parent
enter child
exit child
enter parent
exit parent
So when your exit() function gets called, you may be entering the child right after and if you want to process something when both the parent and child are exited, just acting on the exit() will surely be wrong. Note that the browser is written in such a way that an exit event always happens if an enter event happened. The only exception may be if you close the tab/window in which case they may forfeit sending more events.
So, in the parent exit() function we make use of a setTimeout() call to make an asynchronous call which will happen after the enter() function of a child happens. This means we can set a flag there and test it in the asynchronous function.
MyNamespace = {};
MyNamespace.MyObject = function()
{
var that = this;
// setup parent
jQuery(".parentDiv").hover(
function()
{
that.enter_();
},
function()
{
that.exit_();
});
// setup children
jQuery(".parentDiv .children").hover(
function()
{
that.enterChild_();
},
function()
{
that.exitChild_();
});
}
// couple variable members
MyNamespace.MyObject.prototype.parentEntered_ = false;
MyNamespace.MyObject.prototype.inChild_ = false;
MyNamespace.MyObject.prototype.enter_ = function()
{
// WARNING: if the user goes really fast, this event may not
// happen, in that case the childEnter_() calls us
// so we use a flag to make sure we enter only once
if(!this.parentEntered_)
{
this.parentEntered_ = true;
... do what you want to do when entering (parent) ...
}
};
// NO PROTOTYPE, this is a static function (no 'this' either)
MyNamespace.MyObject.realExit_ = function(that) // static
{
if(!that.inChild_)
{
... do what you want to do when exiting (parent) ...
that.parentEntered_ = false;
}
};
MyNamespace.MyObject.prototype.exit_ = function()
{
// need a timeout because otherwise the enter of a child
// does not have the time to change inChild_ as expected
setTimeout(MyNamespace.MyObject.realExit_(this), 0);
};
// detect when a child is entered
MyNamespace.MyObject.prototype.enterChild_ = function()
{
this.inChild_ = true;
this.enter_(); // in case child may be entered directly
};
// detect when a child is exited
MyNamespace.MyObject.prototype.exitChild_ = function()
{
this.inChild_ = false;
// We cannot really do this, although in case the child
// is "exited directly" you will never get the call to
// the 'exit_()' function; I'll leave as an exercise for
// you in case you want it (i.e. use another setTimeout()
// but save the ID and clearTimeout() if exit_() is not
// necessary...)
//this.exit_()
};

Related

Remove hover event after some time using plain JavaScript

I have a Flip cards, based on: https://www.w3schools.com/howto/howto_css_flip_card.asp.
They work well, nonetheless in mobile (since there's no mouse hover) it expects to tap out in order to the card flip to the unhover state. To prevent that, I want to remove hover event after a certain time has passed using javascript when previously pressed.
This is my current JS code:
let test = document.getElementById("flip-card");
// This handler will be executed only once when the cursor is hovered
test.addEventListener("mouseenter", function( event ) {
console.log('onHover was excecuted'); // debug thing
// reset the hover after a short delay
setTimeout(function() {
test.unbind('mouseenter mouseleave'); //this needs jQuery, is there anyway to do it with plain Js?
}, 3000);
}, false);
Thank you for your advice!
Append Information
I want to "unflip" the "flip-card" after certain time has passed, no matter if the cursor is still there.
I think what you're looking for is removeEventListener, but it has a catch. You can't remove handlers that are set using addEventListener('event', function() { ... }). You have to define your function, meaning your example would look like:
let test = document.getElementById("flip-card");
function handler( event ) {
console.log('onHover was excecuted'); // debug thing
// reset the hover after a short delay
setTimeout(function() {
test.unbind('mouseenter mouseleave'); //this needs jQuery, is there anyway to do it with plain Js?
}, 3000);
}
// This handler will be executed only once when the cursor is hovered
test.addEventListener("mouseenter", handler, false);
removeEventListener must receive the same capture argument that the addEventListener used to add the event in the first place. For example:
test.addEventListener("mouseenter", handler, false);
// Will be removed by
test.removeEventListener('mouseenter', handler, false);
test.removeEventListener('mouseenter', handler, { capture: false });
// But not by
test.removeEventListener('mouseenter', handler, true);
test.removeEventListener('mouseenter', handler, { capture: true });
You can read up more about it on MDN here.
Update
After discussing it more, it turns out the issue was more about changing the "active" state when on mobile vs on desktop.
To achieve this, we can stop using mouse* events and instead use pointer* events. They have the event.pointerType property, which can be either "mouse", "pen", or "touch"
Inside of our handler function, we'll add a conditional to look for pointerType === 'touch'.
function handler(event) {
if (event.pointerType === 'touch') {
// Do stuff
}
}
To avoid too much complexity, we want to leave all the styling up to the CSS you already wrote. All we need need to do is:
a. Mark the body as being "touch compatible".
b. Add a .active class to replace :hover styles.
Our updated function looks like this
function handler(event) {
if (event.pointerType === 'touch') {
event.preventDefault();
document.body.classList.add('pointer-active'); // arbitrary class name
element.classList.add('active');
}
}
Now we just need to remove the class after a certain amount of delay, which we can do with a setTimeout.
function handler(event) {
if (event.pointerType === 'touch') {
event.preventDefault();
document.body.classList.add('pointer-active'); // arbitrary class name
element.classList.add('active');
setTimeout(function() {
element.classList.remove('active');
}, 3000);
}
}
Then we add that back into your original example, it looks like this:
let test = document.getElementById("flip-card");
function handler( event ) {
console.log('onPointerDown was excecuted'); // debug thing
// If the pointer is touch, from mouse, pen, touch
if (event.pointerType === 'touch') {
event.preventDefault();
document.body.classList.add('pointer-active')
// add the active class
test.classList.add('active');
// Remove class after a delay
setTimeout(function() {
test.classList.remove('active');
}, 3000);
}
}
// This handler will be executed only once when the cursor is hovered
test.addEventListener("pointerout", handler, false);
Here's a codepen where you can mess around with it further.

Add event handler and call once immediately

In a web application using jQuery, I have the following code:
$("input[type=checkbox]").on("click", function (event) {
let isChecked = event.currentTarget.checked;
// Do something
});
This will do something whenever the user clicks on a checkbox.
It is necessary to properly initialise the page state by calling this event handler once when loading the page, so it can do whatever would be done on a change of the checkbox already in the beginning.
How can I simply call the added event handler for all matching elements, without actually triggering the event? In the case of "click", this would modify the checkbox state which would break the data.
My current workaround is this:
$("input[type=checkbox]")
.on("click change", function (event) {
let isChecked = event.currentTarget.checked;
// Do something
})
.change();
This adds the event handler for multiple events and only triggers one that has no side effects. But I'd like to limit the event handlers on what's really necessary. If only a "click" event can be used on an element, there are no alternatives. Also, in my case, only elements are hidden or shown, but if something else happens, it might happen twice now.
I'm looking for the onAndNow function in this example pseudocode:
$("input[type=checkbox]").onAndNow("click", function (event) {
let isChecked = event.currentTarget.checked;
// Do something
});
This is another not-so-nice and incomplete solution:
function onCheckboxClicked(event) {
let isChecked = event.currentTarget.checked;
// Do something
}
$("input[type=checkbox]").on("click", onCheckboxClicked);
onCheckboxClicked(???);
You can trigger the function on each of the elements using this code:
function onCheckboxClicked(event) {
if (event == 0) {
console.log("onload")
} else {
console.log("event")
// Do something
}
console.log($(this).prop('checked'))
}
// Event driven
$("input[type=checkbox]").on("click", onCheckboxClicked);
// Executed on load
$("input[type=checkbox]").each(onCheckboxClicked);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="checkbox" />
Note: As you can see, you won't be able to pass an event with an onload, so you will have to write code for each situation. You can, however, access the element with $(this).

click outside DIV

<body>
<div id="aaa">
<div id="bbb">
</div>
</div>
</body>
$(#?????).click(function(){
$('#bbb').hide();
})
http://jsfiddle.net/GkRY2/
What i must use if i want hide #bbb if user click outside box #bbb? But if i click on div #bbb then box is still visible - only outside.
$('body').click(function(e){
if( e.target.id == 'bbb' )
{ return true; }
else
{ $('#bbb').hide(); }
});
A note of explanation: There are a few ways to do this, either way we need to listen for a click on a parent element, weather it be a direct parent like #aaa or a distant parent like the body or the document. This way we can capture clicks that occur outside of #bbb.
Now that we have that we need the .hide to NOT occur if the user did click inside of #bbb. We can do this two ways
Stop propagation if the user clicks on #bbb. This will make the click event not 'bubble' up to the parent. That way the click event never reaches the parent and so #bbb will not hide. I personally don't like this method because stop propagation will so ALL click events from bubbling, and you may have click events that you would like to bubble to a local parent and not a distant parent. Or you may have listeners delegated from a distant parent, which will stop working if click propagation is stopped.
Check for the #bbb element in the parent listener. This is the method shown above. Basically this listens on a distant parent, and when a click occurs it checks to see if that click is on #bbb specifically. If it IS NOT on #bbb .hide is fired, otherwise it returns true, so other things that may be tied into the click event will continue working. I prefer this method for that reason alone, but secondarily its a-little bit more readable and understandable.
Finally the manner in which you check to see if the click originated at #bbb you have many options. Any will work, the pattern is the real meat of this thing.
http://jsfiddle.net/tpBq4/ //Modded from #Raminson who's answer is very similar.
New suggestion, leverage event bubbling without jQuery.
var isOutSide = true
bbb = documment.getElementById('bbb');
document.body.addEventListener('click', function(){
if(!isOutSide){
bbb.style.display = 'none';
}
isOutSide = true;
});
bbb.addEventListener('click', function(){
isOutSide = false;
});
Catch the click event as it bubbles-up to the document element. When it hits the document element, hide the element. Then in a click event handler for the element, stop the propagation of the event so it doesn't reach the document element:
$(function () {
$(document).on('click', function () {
$('#bbb').hide();
});
$('#bbb').on('click', function (event) {
event.stopPropagation();
});
});
Here is a demo: http://jsfiddle.net/KVXNL/
Docs for event.stopPropagation(): http://api.jquery.com/event.stopPropagation/
I made a plugin that does this. It preserves the value for this where as these other solutions' this value will refer to document.
https://github.com/tylercrompton/clickOut
Use:
$('#bbb').clickOut(function () {
$(this).hide();
});
You can use target property of the event object, try the following:
$(document).click(function(e) {
if (e.target.id != 'bbb') {
$('#bbb').hide();
}
})
DEMO
This will work
$("#aaa").click(function(){
$('#bbb').hide();
});
$("#bbb").click(function(event){
event.stopPropagation();
})​
Becouse bbb is inside the aaa the event will "bubbel up to aaa". So you have to stop the bubbling by using the event.stopPropagation when bbb is clicked
http://jsfiddle.net/GkRY2/5/
OK
* this is none jquery. you can easly modify it to work with IE
first create helper method to facilitate codding don't get confused with JQuery $()
function $g(element) {
return document.getElementById(element);
}
create our listener class
function globalClickEventListener(obj){
this.fire = function(event){
obj.onOutSideClick(event);
}
}
let's say we need to capture every click on document body
so we need to create listeners array and initialize our work. This method will be called on load
function initialize(){
// $g('body') will return reference to our document body. parameter 'body' is the id of our document body
$g('body').globalClickEventListeners = new Array();
$g('body').addGlobalClickEventListener = function (listener)
{
$g('body').globalClickEventListeners.push(listener);
}
// capture onclick event on document body and inform all listeners
$g('body').onclick = function(event) {
for(var i =0;i < $g('body').globalClickEventListeners.length; i++){
$g('body').globalClickEventListeners[i].fire(event);
}
}
}
after initialization we create event listener and pass reference of the object that needs to know every clcik on our document
function goListening(){
var icanSeeEveryClick = $g('myid');
var lsnr = new globalClickEventListener(icanSeeEveryClick);
// add our listener to listeners array
$g('body').addGlobalClickEventListener(lsnr);
// add event handling method to div
icanSeeEveryClick.onOutSideClick = function (event){
alert('Element with id : ' + event.target.id + ' has been clicked');
}
}
* Take into account the document body height and width
* Remove event listeners when you don't need them
$(document).click(function(event) {
if(!$(event.target).closest('#elementId').length) {
if($('#elementId').is(":visible")) {
$('#elementId').hide('fast');
}
}
})
Change the "#elementId" with your div.

Prevent calling a function

I have two parts of scripts.
Part 1 :
$("mySelector").click(function() {
alert('you call me');
})
Part 2 :
$("mySelector").click(function() {
if(myCondition) {
//how can i prevent calling the first function from here ???
}
})
The whole problem, is that i have no access to part1. So i need to unbind the event allready specified in part 1, if myCondition is true, but otherwise i need to call the first function.
Thanks
UPDATE:
Thank you. I didn't know about stopImmediatePropagation(). But i feel, that there must be something like that :)
But actually in my case it doesn't work :(
Please have a look at my site
http://www.tours.am/en/outgoing/tours/%D5%80%D5%B6%D5%A4%D5%AF%D5%A1%D5%BD%D5%BF%D5%A1%D5%B6/Park-Hyatt-Goa/
Under the hotel description tab i have cloud carousel, when i click on not active image (not the front image), as you can see i'm consoling that i stopImmediatePropagation() there, but the event however calls :(
If your handler is registered first, then you can use event.stopImmediatePropagation like this:
$("mySelector").click(function(event) {
if(myCondition) {
event.stopImmediatePropagation();
}
})
Be aware that this will also stop event bubbling, so it will also prevent click handlers on parent elements from being invoked.
Update: If this does not work, then your handler is attached after the one you want to control. This is a problem that makes the solution much more difficult. I suggest seeing if you can bind "before the other guy", otherwise you will have to unbind the existing handler and then conditionally invoke it from within your own by retaining a reference to it. See jQuery find events handlers registered with an object.
No access:
$("#mySelector").click(function() {
alert('you call me');
})
Access:
var myCondition = true, //try false too
fFirstFunction = $("#mySelector").data("events").click[0].handler;
$("#mySelector").unbind("click");
$("#mySelector").click(function() {
if(myCondition) {
alert(myCondition);
} else {
$("#mySelector").click(fFirstFunction);
}
});
Look at this example
You can call
$('mySelector').unbind('click');
to get rid of all the click handlers. If your script is loaded after the other one (which appears to be the case), then that should do it. However note that it does unbind all "click" handlers, so make sure you call that before you add your own handler.
If you can't ensure your handler is attached first, try the following code:
var events = $('mySelector').data("events"); //all handlers bound to the element
var clickEvents = events ? events.click : null;//all click handlers bound to the element
$('mySelector').unbind('click'); //unbind all click handlers
//bind your handler
$("mySelector").click(function(e) {
if (myCondition) {
//do what you want
} else {
//call other handlers
if (clickEvents) {
for (var prop in clickEvents)
clickEvents[prop].call(this, e);
}
}
})
Update:
Above code is for jQuery 1.3.2
Above code is based on internal implementation of jQuery 1.3.2, so please check it carefully once you update jQuery.
return false;
-or-
event.preventDefault();

jQuery: Get reference to click event and trigger it later?

I want to wrap an existing click event in some extra code.
Basically I have a multi part form in an accordion and I want to trigger validation on the accordion header click. The accordion code is used elsewhere and I don't want to change it.
Here's what I've tried:
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
//Get reference to original click
var originalClick = currentAccordion.click;
//unbind original click
currentAccordion.unbind('click');
//bind new event
currentAccordion.click(function () {
//Trigger validation
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
//Call original click.
originalClick();
}
});
});
jQuery throws an error because it's trying to do this.trigger inside the originalClick function and I don't think this is what jQuery expects it to be.
EDIT: Updated code. This works but it is a bit ugly!
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
var originalClick = currentAccordion.data("events")['click'][0].handler;
currentAccordion.unbind('click');
currentAccordion.click(function (e) {
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
$.proxy(originalClick, currentAccordion)(e);
}
});
});
I think this:
var originalClick = currentAccordion.click;
Isn't actually doing what you think it is - you're capturing a reference to the jQuery click function, rather than event handler you added, so when you call originalClick() it's equivalent to: $(value).click()
I finally came up with something reliable:
$(".remove").each(function(){
// get all our click events and store them
var x = $._data($(this)[0], "events");
var y = {}
for(i in x.click)
{
if(x.click[i].handler)
{
y[i] = x.click[i].handler;
}
}
// stop our click event from running
$(this).off("click")
// re-add our click event with a confirmation
$(this).click(function(){
if(confirm("Are you sure?"))
{
// if they click yes, run click events!
for(i in y)
{
y[i]()
}
return true;
}
// if they click cancel, return false
return false;
})
})
This may seem a bit weird (why do we store the click events in the variable "y"?)
Originally I tried to run the handlers in x.click, but they seem to be destroyed when we call .off("click"). Creating a copy of the handlers in a separate variable "y" worked. Sorry I don't have an in depth explanation, but I believe the .off("click") method removes the click event from our document, along with the handlers.
http://www.frankforte.ca/blog/32/unbind-a-click-event-store-it-and-re-add-the-event-later-with-jquery/
I'm not a jQuery user, but in Javascript, you can set the context of the this keyword.
In jQuery, you use the $.proxy() method to do this.
$.proxy(originalClick, value);
originalClick();
Personally, I'd look at creating callback hooks in your Accordion, or making use of existing callbacks (if they exist) that trigger when opening or closing an accordion pane.
Hope that helps :)
currentAccordion.click is a jQuery function, not the actual event.
Starting with a brute-force approach, what you'd need to do is:
Save references to all the currently bound handlers
Unbind them
Add your own handler, and fire the saved ones when needed
Make sure new handlers bound to click are catched too
This looks like a job for an event filter plugin, but I couldn't find one. If the last point is not required in your application, then it's a bit simpler.
Edit: After some research, the bindIf function shown here looks to be what you'd need (or at least give a general direction)

Categories

Resources