Key event binding at table level - javascript

I have 2 tables on 1 page. I would like the user to be able to click the table and then use the arrow keys to navigate to previous and next. The only way I have been able to get the keyup events to fire in all browsers is by attaching them directly to the 'document'. I have wired up an event to add a 'grid-focused' class to the grid that is in focus and I tried using that as the selector on my events but I cannot get any event action then.
Event Binding
function attachInitEvents() {
if ($self.config.pageHotKeysEnabled) {
var keyNav = function(e) {
if (e.keyCode == 37 && paging.hasPrevious) { // left
$self._log.info('Left arrow key pressed.');
fetchData('prev');
}
if (e.keyCode == 39 && paging.hasNext) { //right
$self._log.info('Right arrow key pressed.');
fetchData('next');
}
}
var $doc = $(document);
// hot key support
$doc.off('keyup');
$doc.on('keyup', keyNav);
}
}
I am having another issue, where I am doing $doc.off('keyup'). I think this is part of the problem as it is unbinding all keyup events, what I really want is for it to just unbind the event I am trying to attach if it exists. In jQuery docs it technically says I should be using $doc.off('keyup', keyNav); to unbind it, but it does not unbind the event and I get multiples of it.
So core questions are,
Can I trigger a KeyUp event without binding to the document and can I do it from the table level?
How can I properly make sure I am not rebinding over and over again? Now, if this page has 2 tables, I guess I would expect there
to be 2 events one for each table and then the .grid-focused class
would be what allows the event to trigger or not?
Here is a Fiddle: http://jsfiddle.net/93fp293w/
Basically, I have no input boxes it is just spans, I am basically making it easier for them to page through all the data by just using the arrow keys. I believe the fiddler is pretty accurate to the situation. I feel what I am asking is not possible since I wont have an actual control in focus and if that is the case I can accept that but I guess I am hoping there is a work around.

The problem with attaching keyup is that, the parent div has to be mutable(contenteditable). else you have to attach to the document.
I have updated fiddle, might be a hack let me know if this works for you ?
http://jsfiddle.net/puneethp/qfpyeydg/3/
$(document).on("keyup", function (e) {
e.target = $(".focus")[0];
..
}
if you have only one column focused at a time, my previous fiddle will still work.

Related

Why is a nested event handler still firing if the parent event isn't being triggered, in jQuery?

In my site, I have the following snippet of jQuery/JavaScript:
$("span.testLink").mousedown(function (event) {
if (event.which == 1) {
event.stopPropagation();
$("span.testLink").mouseup(function (e) {
if (e.which == 1) {
var urlEncoded = encodeURI($(this).parent().find("input").val());
window.open(urlEncoded);
}
});
$(this).css("color", "#0b3773");
}
});
It's pretty easy to tell what I expect to happen here (even though, clearly, my expectations are off somewhere), but for the sake of completeness: There is a link which should act as if it had the simple .click() event handler (because the mouseup() is nested within the mousedown() or so I thought). Apparently, this is not the way this works because I can click and hold down the left mouse button outside of the span.testLink element, drag back over the span.testLink element, then release the left mouse button and the $("span.testLink").mouseup() event still fires.
Clearly, I am in err as to when I expect this nested event to fire. My question is why and how should I be doing this differently?
Additional Information:
The span.testLink element, among others, are nested within a jQueryUI sortable element. Not sure if this matters or not, but thought it was definitely worth mentioning. If the answer to this question is "Nope, this should work. Check your plugin.", that is perfectly fine. I just need to make sure I understand how nested event handlers in jQuery work before I end up fouling something else up, if that is, indeed, the case.

Comparing jQuery (javascript?) Events for Equal Origin Click

I'm comparing click events in jQuery to see if they fired from the same click. Right now I'm using the events' timeStamp to test to see if the events came from the same click, and while this will almost certainly work, I'm wondering if there's a better way.
For example, if body and header both have a click event, and I click on the header, it will bubble up to the body. What is the best way to test if both events that fire come from the same click?
In plain JavaScript, the same event object will reach both event listeners, so you can compare them. jQuery on the other hand will wrap the original event object into a new normalized object for each listener, so you have to check their .originalEvent property instead.
In any case, I never needed to check for that. Usually, you just want to know if the clicked element is the same as the one you bound the click to, or not:
document.body.addEventListener('click', function(e) {
if(e.target === this) {
// the click happened on the body itself
} else {
// the click bubbled up
}
}, false);
jQuery provides a nice mechanism for event delegation, so you don't even need the if:
$('body').on('click', '.someSelector', function(e) {
// This will only fire if an element with class 'someSelector'
// was clicked
});

Can mouseenter and click event exist together?

I am wondering if mouseenter and click event can exist together and they can both exist to TRUE when checked with:
if ((evt.type === 'mouseenter') && (evt.type === 'click'))
It is because when I mouse over the link, the mouseenter triggers (set to TRUE) and even when I clicked on it, the hover is still shown. Probably they could exist together but I'm no expert on this.
If someone can give insights, I would appreciate it a lot.
Also how can I trigger the click event during the mouseenter event?
The mouseenter event fires when the mouse enters the control. The click event fires when the mouse is clicked. They are two separate events which call two separate event handlers. If you click just as the mouse enters the element they will be called within a short timespan of one another but they are still two distinct events.
It is also important that you differentiate between the mouseenter and the mouseover events. mouseenter fires when the mouse physically enters an element, whereas mouseover fires continually while the mouse remains over an element.
While you cannot trigger the click event per se, you can call the same function that is called by the click event handler. For example if you have this:
var myfunc = function (e) { ... }
document.getElementById("id").onclick = myfunc;
Then you could simply call myfunc directly and you would get the same result as if the mouse was clicked.
They can 100% exist together, and this is a great question with no good answer... When you're on a mobile device, a mouseenter event will be thrown on tap... If you are also detecting onclick as well as mouseenter, then there will be a discrepancy between mobile devices and desktop machines.
It's kind of hard to solve such a small issue at the moment.
const x = document.getElementById('some_node')
x.onclick=(e)=>{
e.stopPropagation()
// this logic will be triggered on click for both desktop and mobile
}
x.onmouseenter=(e)=>{
e.stopPropagation()
// this logic will be triggered on click for mobile only (but will
//have already been triggered on desktop when cursor entered node)
}
The only workaround I came up for this, and I think it's pretty clever, is using a eventlistener for taps/touches. The order/priority that these events are fired goes: touch > mouseenter > click.
Since the touch event is fired first, you can add a touch event listener (which will only register on a mobile device), and change a variable that prevents the mouseenter event from being triggered (which is the logic that would generally be conflicting with the onclick logic)... like this:
let isMobile = false
x.addEventListener('touchstart',(e)=>{
isMobile = true
}, false);
Then your mouseenter would need to look like this:
x.onmouseenter=(e)=>{
e.stopPropagation()
if(!isMobile){
// this logic will no longer cause a conflict between desktop and mobile
}
}
they can exist on the same object, think a button with a hover state and then a click action. The click event, though will only read the click event since the enter event actually occurred earlier.
You can create a var like mouseIsOverand set it to true when the enter event fires. I can be safely assumed, though that if a click happens the mouse is over the same target.
The two events may happen at the same time, but they will still be processed on after the other. So the if you posted will never evaluate to true.
If you look at your code again you can see that it doesn't make sense. How can something be X and Y at the same time? It can't.
for the first question i think u got an answer....
however, for Also how can I trigger the click event during the mouseenter event?
u can use trigger() function..
http://jsfiddle.net/PDhBW/2/
if u want to read more about trigger
here is the link
http://api.jquery.com/trigger/
With Jquery event delegation, You can use binding multiple events at once
$('#IdElement').on('mouseenter click', function () {
//Your Code
});
http://jqfundamentals.com/chapter/events

Can jQuery click events be unbound or reset?

I've discovered a resource leak on a webpage I'm working on.
This webpage has two textfields, that upon click show a modal dialog, perform a data request to the backend, and then present that information in a table that the user can select an entry from for use in the textbox they originally clicked.
I'm binding the click events to the textboxes like so:
var $field = $('#one-of-the-text-fields');
$field.click(function () {
App.DialogClass.show();
App.DialogClass.PopulateTable();
App.DialogClass.GotoPageButtonAction(actionArgs); // Offender!
});
...Which calls...
App.DialogClass = (function($) {
var pub {},
$gotoPage = $('#pageNumberNavigationField'),
$gotoPageButton = $('#pageNumberNavigationButton');
// ...SNIP unimportant other details...
pub.GotoPageButtonAction = function (args) {
$gotoPageButton.click(function () {
var pageNumber = $gotoPage.val();
pub.PopulateTable(args); // Breakpoint inserted here...
});
};
return pub;
})(jQuery);
I noticed the leak because when I ran through using Chrome's JavaScript debugger, I'm always having one extra breakpoint hit every time I click a different button (e.g. the first time I click field A, the breakpoint is hit twice. When I hit field B after that, the break point is hit three times. If I click A after that, the breakpoint is hit four times. Extrapolate as necessary.)
Nowhere in my code am I doing anything about an existing click event for a given field. I suspect my leak stems from the fact that the events are not getting cleaned up. That being said, I am also not terribly familiar with JavaScript/jQuery. What are some techniques for removing click events from a control?
Sure. Just unbind them:
$field.unbind('click');
However, bear in mind that this will remove all event handlers for click, not just yours. For safety, you should use namespaces when binding handlers:
$field.bind('click.mynamespace', function(){
// do something
});
Then,
$field.unbind('click.mynamespace');
So, then, only your handler will be removed.
If you have used .bind() to bind them .unbind() removes the events
If you have used .on() to bind them .off() removes the events
JQuery offers the unbind function to unbind event listeners.
Note that you may also do it in vanilla JS using removeEventListener.
But instead of unbinding, you probably should not bind each time in GotoPageButtonAction : once is enough.

How do I prevent scrolling with arrow keys but NOT the mouse?

Since I'm using jQuery, any solution via that would work too. Ideally, I'd like to know both, though.
I already have the arrow keys bound to another function on my page (using jQuery), but having them cause the page to scroll in addition to that, causes me problems.
I may have known this at one time, but I don't remember it anymore.
Adding document level keypress handler does the trick!
var ar=new Array(33,34,35,36,37,38,39,40);
$(document).keydown(function(e) {
var key = e.which;
//console.log(key);
//if(key==35 || key == 36 || key == 37 || key == 39)
if($.inArray(key,ar) > -1) {
e.preventDefault();
return false;
}
return true;
});
Under some circumstances, eg. when you don't actualy have an element you focus on, just some area you had to click, you might not have too much control over the handler and preventing the event at the global level can be a little flaky at best (as I found out the hard way).
The simplest solution in those cases is to bind on the click even of the control button and focus on a empty input element which you position -9000px to the left.
You can then reliably block the event via keydown and also dont have to worry about blocking default behavior or other global listeners since the default behavior on the input element will just move the cursor to the left and right.
If you add a document level keypress handler it will prevent normal scroll on the page at any time, not only when your element has the focus, this might be an undesired effect.

Categories

Resources