I have a js function inside a web page that trap the position of the click on it. I had to implement it in a way that is recognized if the page contain a form element, and, if so, do a check as to whether that the click that I'm recording is made on one at random form element or not.
I got up to check the form and to learn its elements, but I do not know how to check if the click is made on a single element:
$('*').on('mousedown', function (e) {
// make sure the event isn't bubbling
if (e.target != this) {
return;
}
// do something with the click somewhere
// for example, get the coordinates:
x = e.pageX;
y = e.pageY;
pa = $(location).attr('href');
//Window dimension
var width = $(window).width();
//var width = $getWidth();
var height = $(window).height();
//Check if page contain a form
var elementF = document.querySelectorAll('form');
var fname = '';
var elements = '';
for (var i=0; i<elementF.length; i++){
fname = elementF[i].id;
elements = document.forms[fname].elements;
for (x=0; x<elements.length; x++){
//Now i need to check if element is clicked or not
}
}
What you're doing here is binding a mousedown event to every element on the page. This will be resource intensive and should not be necessary. Also, since it's bound everywhere, the if (e.target !== this){ return; } does prevent it from firing a million times whenever you mousedown on something, but also prevents it from bubbling. Just let it bubble and use e.target and e.currentTarget to tell stuff apart. There shouldn't be any reason to do $('*').on("mousedown")
Why can't you just do this?
$("form").on("click", function(e){
// e.currentTarget will be the form element capturing the click
console.log( $(e.currentTarget).html() );
// e.target will be whatever element you clicked on
// so you can do whatever you want with it
console.log( $(e.target).text() );
// All your handler code here
});
Some possibly helpful stuff
http://api.jquery.com/event.currenttarget/
Is there a difference between $(e.currentTarget) and $(this)?
http://learn.jquery.com/events/event-delegation/
Try
$('*').on('mousedown', function (e) {
var elementClicked = e.target.nodeName;
console.log("element: " + elementClicked)
})
Related
I've developed a javascript drag and drop that mostly uses the standard 'allowdrop', 'drag' and 'drop' events.
I wanted to customise the 'ghosted' dragged object, so I've added a display:none div that get populated with the innerHTML of the draggable element and made visible (display:block;) when the user starts dragging.
The draggable div is absolutely positioned and matches the mouse movements. For this I needed to add 3 event listeners to document.body. They are as follows:
document.body.addEventListener('dragover', function (ev) {
console.log("dragover event triggered");
ev = ev || window.event;
ev.preventDefault();
dragX = ev.pageX;
dragY = ev.pageY;
document.getElementById("dragged-container").style.left = (dragX - dragOffsetX) + "px";
document.getElementById("dragged-container").style.top = (dragY - dragOffsetY - 10) + "px";
if (mostRecentHoveredDropTargetId!="") {
if (dragX<mostRecentHoveredDropTargetRect.left || dragX>mostRecentHoveredDropTargetRect.right || dragY<mostRecentHoveredDropTargetRect.top || dragY>mostRecentHoveredDropTargetRect.bottom) {
document.getElementById(mostRecentHoveredDropTargetId).classList.remove("drop-target-hover");
mostRecentHoveredDropTargetId = "";
}
}
});
document.body.addEventListener('drop', function (ev) {
console.log("drop event triggered");
ev.preventDefault();
var data = ev.dataTransfer.getData("text"); // data set to the id of the draggable element
if (document.getElementById(data)!=null) {
document.getElementById(data).classList.remove("dragged");
document.getElementById("dragged-container").innerHTML = "";
document.getElementById("dragged-container").style.display = "none";
var draggablesClasses = document.getElementById(data).className;
if ((draggablesClasses.indexOf('draggable')==-1 || draggablesClasses=="") && document.getElementById(data).getAttribute('draggable')=="true") {
if (draggablesClasses=="") {
document.getElementById(data).className += "draggable";
} else {
document.getElementById(data).className += " draggable";
}
}
}
});
// resets dragged-container and origin .draggable, when mouse released outside browser window
document.body.addEventListener('mouseleave', function (ev) {
if (jqueryReady==true) {
$(".dragged").addClass("draggable");
$(".dragged").removeClass("dragged");
}
document.getElementById("dragged-container").innerHTML = "";
document.getElementById("dragged-container").style.display = "none";
});
This is all working fine. The drag and drop performs exactly as I expect.
The problem is when I go to another page, obviously those body event listeners are still running.
I've seen a number of answers here and have tried everything I've seen. For starters this:
window.onunload = function() {
console.log("about to clear event listeners prior to leaving page");
document.body.removeEventListener('dragover', null);
document.body.removeEventListener('drop', null);
document.body.removeEventListener('mouseleave', null);
return;
}
...but the console.log output doesn't even appear (let alone the 'null's being wrong, I'm pretty sure). I've also tried this, in the jQuery ready function:
$(window).bind('beforeunload', function(){
console.log("about to clear event listeners prior to leaving page");
document.body.removeEventListener('dragover', null);
document.body.removeEventListener('drop', null);
document.body.removeEventListener('mouseleave', null);
});
..but, once again, the console isn't even receiving that output.
I have also tried both the above with 'onbeforeunload' AND 'onunload'.
What am I doing wrong? - specifically to do with removing these window.body event listeners, I mean (Anything else I can sort out later).
Thanks.
removeEventListener requires the handler
Don't use anonymous functions is the solution.
Like this:
var dragHandler = function (ev) {
console.log("dragover event triggered");
};
document.body.addEventListener('dragover', dragHandler);
and after:
window.onunload = function() {
console.log("about to clear event listeners prior to leaving page");
document.body.removeEventListener('dragover', dragHandler);
return;
}
I have a list of controls contained in a parent div called overlay-controls.
There is many list controls that each have their own overlay-controls.
I am using a for loop to add the event listener to each button that contains the class delete.
Before the user can delete the item, they must confirm. I am trying to attach this to every delete button found in overlay-controls.
I got it to work using a for loop but I know there is a better way using bubbling and capturing. I am having trouble targeting only the delete class inside overlay-controls by bubbling up to parent div.
See the live demo here by clicking on each delete button: http://jsfiddle.net/8qqfeoa2/1/
Here is my code using the for loop:
(function() {
function getConfirmation(e){
var retVal = confirm("Are you sure you want to delete this request?");
if( retVal == true ){
return true;
}else{
e.preventDefault();
return false;
}
}
var del = document.querySelectorAll('.delete');
for(var i = 0, len = del.length; i < len; i++){
del[i].addEventListener('click', function(e) {
getConfirmation(e);
}, false);
}
}());
You dont event need the For / .each loop
Jquery takes care of it internally
$('.delete').on('click', function(e){
getConfirmation(e);
});
Provided you are using jQuery and in getConfirmation method you may also get that specific (clicked) element by using e.target which returns the target on which click happened.
Only Javascript solution
As you requested one
var deletebuttons = document.getElementsByClassName('delete');
for(var button in deletebuttons) {
button.onclick = getConfirmation;
}
I'm working on a project that's using a sort of dummy pagination. The body is set to overflow: hidden and currently the only way to navigate the pages is by physically clicking on either links in the nav pane, or on sroll-down/scroll-up buttons. Here's an idea of the events that are triggered when those elements are physically clicked:
var links = $('#topnav, .top-mid a'), l = links.length - 1;
var id = 0;
$('.scrollDown, .scrollUp, .top-mid a, body.home #topnav').click(function (e) {
e.preventDefault();
var $this = $(this);
if ($this.is('.scrollDown') && id < l) id++;
if ($this.is('.scrollUp') && id > 0) id--;
if ($this.is('#topnav, .top-mid a')) id = links.index(this);
// Body is animated down or up and elements are
// shown or hidden depending on what was clicked and
// and what the var id is currently equal to
});
The idea is to trigger exactly ONE click of the scroll button on a mousewheel event. So something close to as simple as this, but that actually works:
$(window).on('mousewheel', function(e){ // I realize this will not work in FF
var evt = e.originalEvent.wheelDelta;
console.log(evt);
// Scrolling Down
if (evt < 0) {
$('.scrollDown').click(); // This fires recursively as long as wheelDelta !== 0
}
});
How can I either force wheelDelta to only increment or decrement by one, or, barring that, how can I eliminate the recursion on the click event?
I've been at this for a while, and read lots of posts and haven't been able to crack it. I've also tried fullPage.js, but it's rather heavy and doesn't really suit my project for other various reasons.
I finally solved this, and of course it turned out to be quite simple. It was a matter of toggling a boolean value inside the click() event, but only after all the animations had taken place. Like this:
var scrolled = false;
$(window).on('mousewheel', function(e){
var evt = e.originalEvent.wheelDelta;
// Scrolling Down - Only fire the click event if it hasn't already fired
if (evt < 0 && !scrolled) {
$('.scrollDown').click();
// Scrolling Up
} else if (evt > 0 && !scrolled) {
$('.scrollUp').click();
}
});
// Toggle the scrolled variable inside the original click event
$('.scrollDown, .scrollUp').click(function (e) {
e.preventDefault();
var $this = $(this);
if ($this.is('.scrollDown') && id < l) {
id++;
scrolled = true;
setTimeout(function(){
scrolled = false;
}, 1500);
}
if ($this.is('.scrollUp') && id > 0) {
id--;
scrolled = true;
setTimeout(function(){
scrolled = false;
}, 1500);
}
// Other events here
// The timeout has to be set high enough to assure
// that the mousewheel event is finished
});
When I do this,
location.hash = "test"
the url is updated and the page homes in on the element with that id.
Is there a way to stop the page from homing in to that element?
Solution
You cannot prevent this behaviour, but you can fool it by temporarily hiding the target, eg. like that (it has nothing to do with jQuery):
// obtain "target" DOM (not jQuery) element and do this:
var original_id = target.id; // save original ID
target.id = null; // set target's ID
location.hash = 'test'; // issue location.hash change
target.id = original_id; // set ID to original value
Generalized solution
Or more general example:
// make target_hash set to a hash you want to not lead you to page section and:
var element = document.getElementById(target_hash); // find element by ID
var original_id = element.id; // save original ID
location.hash = target_hash; // previously defined by you (eg. 'test')
element.id = original_id; // reset ID
Demo / proof
The live example can be as follows, in the event handler attached through jQuery (demo here: http://jsfiddle.net/DaZfH/):
some_link.on('click', function(event){
event.preventDefault();
var target = document.getElementById('target');
var original_id = target.id;
target.id = null; // unset ID
location.hash = 'target';
target.id = original_id;
});
Disclaimer
But indeed others are right: moving you to the correct place in the document is the correct behaviour. If you are doing things like I mentioned, then your solution is pretty hakish and there is definitely a better way to do that.
Is there a way to stop the page from homing in to that element
Yes. Although the hashchange event is not cancelable, you can reset its unwanted behavior like this.
var popY,
popX;
//When location.hash is changed, popstate is the first event to fire
//Use this event to record current state
window.addEventListener('popstate', popstateHandler);
function popstateHandler() {
popY = window.scrollY;
popX = window.scrollX;
}
//Reset scroll position with recorded values when hashchange fires (after page is scrolled)
window.addEventListener('hashchange', hashchangeHandler);
function hashchangeHandler() {
window.scrollTo(popX, popY);
}
That's the basis of it. You might want to do some proofing for IE, and implement your reasons for doing this: Animate scroll, active something etc..
Polyfill for scrollY, scrollX:
if(!('scrollY' in window)) {
Object.defineProperty(window, 'scrollY', {
get: function () {
return window.pageYOffset
}
});
Object.defineProperty(window, 'scrollX', {
get: function () {
return window.pageXOffset
}
})
}
I am using jquery to keep the focus on a text box when you click on a specific div. It works well in Internet Explorer but not in Firefox. Any suggestions?
var clickedDiv = false;
$('input').blur(function() { if (clickedDiv) { $('input').focus(); } });
$('div').mousedown(function() { clickedDiv = true; })
.mouseup(function() { clickedDiv = false });
Point to note: the focus() method on a jquery object does not actually focus it: it just cases the focus handler to be invoked! to actually focus the item, you should do this:
var clickedDiv = false;
$('input').blur( function() {
if(clickeddiv) {
$('input').each(function(){this[0].focus()});
}
}
$('div').mousedown(function() { clickedDiv = true; })
.mouseup(function() { clickedDiv = false });
Note that I've used the focus() method on native DOM objects, not jquery objects.
This is a direct (brute force) change to your exact code. However, if I understand what you are trying to do correctly, you are trying to focus an input box when a particular div is clicked when that input is in focus.
Here's my take on how you would do it:
var inFocus = false;
$('#myinput').focus(function() { inFocus = true; })
.blur(function() { inFocus = false; });
$('#mydiv').mousedown(function() {
if( inFocus )
setTimeout( function(){ $('#myinput')[0].focus(); }, 100 );
}
Point to note: I've given a timeout to focussing the input in question, so that the input can actually go out of focus in the mean time. Otherwise we would be giving it focus just before it is about to lose it. As for the decision of 100 ms, its really a fluke here.
Cheers,
jrh
EDIT in response to #Jim's comment
The first method probably did not work because it was the wrong approach to start with.
As for the second question, we should use .focus() on the native DOM object and not on the jQuery wrapper around it because the native .focus() method causes the object to actually grab focus, while the jquery method just calls the event handler associated with the focus event.
So while the jquery method calls the focus event handler, the native method actually grants focus, hence causing the handler to be invoked. It is just unfortunate nomenclature that the name of this method overlaps.
I resolved it by simply replace on blur event by document.onclick and check clicked element if not input or div
var $con = null; //the input object
var $inp = null; // the div object
function bodyClick(eleId){
if (eleId == null || ($inp!= null && $con != null && eleId != $inp.attr('id') &&
eleId != $con.attr('id'))){
$con.hide();
}
}
function hideCon() {
if(clickedDiv){
$con.hide();
}
}
function getEl(){
var ev = arguments[0] || window.event,
origEl = ev.target || ev.srcElement;
eleId = origEl.id;
bodyClick(eleId);
}
document.onclick = getEl;
hope u find it useful