Issues with event.preventDefault() not working - javascript

looking for some advice around the correct usage of preventDefault() - code is below. In short, I don't want the a.dropdownTrigger click event to jump to the anchor. I thought inserting event.preventDefault() as the first line of that function would do the trick, but apparently not.
Thinking it might have something to do with having bound the hashchange event, which I'm using to monitor changes to the url (clicking the a.dropdownTrigger element updates the hash location, which calls the listener on hashchange - doing it this way enables me to catch inbound links to a dropdownTrigger, and maintain url history).
Any ideas where I'm going wrong?
// check inbound anchor links against dropdown ids
if (hash != "" && dropdowns.length != 0 && $.inArray(hash, dropdowns)) {
OpenDropdownForHash(hash);
}
// listen for hashchange once page loads (handles on-page links to dropdown content)
$(window).bind('hashchange', function () {
hash = window.location.hash;
if (dropdowns.length != 0 && $.inArray(hash, dropdowns)) {
OpenDropdownForHash(hash);
}
});
// open the targeted dropdown - var incoming is a bool, differentiate between inbound links and on-page clicks
function OpenDropdownForHash(x) {
$(x).next('.dropdown').toggleClass('open').slideToggle(200);
if ($(x).next('.dropdown').hasClass('open')) { //can this live in the callback above?
$(x).parent().css("-webkit-transition", "all 0.8s ease")
.css("backgroundColor", "white")
.css("-moz-transition", "all 0.8s ease")
.css("-o-transition", "all 0.8s ease")
.css("-ms-transition", "all 0.8s ease")
.css("backgroundColor", '#eeeeee').delay(600).queue(function () {
$(this).css("backgroundColor", "white");
$(this).dequeue(); //Prevents holding color with no fadeOut on second click.
});
}
}
// finally, the basic click handler for dropdowns - update the hash (to allow history), which triggers previously bound hashchange event
$('a.dropdownTrigger').bind('click',function (e) {
e.preventDefault();
if ("#" + $(this).attr('id') == location.hash) { // clicking an open dropdown link doesn't trigger the hashchange event, so we check manually
OpenDropdownForHash("#" + $(this).attr('id'));
}
else { // clicking a closed dropdown does call hashchange, so the OpenDropdownForHash function is called by the listener
location.hash = $(this).attr('id');
}
});
UPDATE: Solved this with a bit more effort, reworked the click handler and simplified the hashchange listener:
if (dropdowns.length != 0) {
// the basic click handler for dropdowns - update the hash (to allow history), which triggers previously bound hashchange event
$('#mainContentContainer a').bind('click', function (e) {
var target = $(this).attr('href') != null ? $(this).attr('href') : "#" + $(this).attr('id');
var offset = window.pageYOffset;
if ($.inArray(target, dropdowns) && location.hash != target) {
location.hash = target;
window.scrollTo(0, offset);
}
else if ($.inArray(target, dropdowns) && location.hash == target) {
OpenDropdownForHash(location.hash, $(this).hasClass('dropdownTrigger'));
window.scrollTo(0, offset);
}
});
}
$(window).bind('hashchange', function(e) {
OpenDropdownForHash(location.hash, $(this).hasClass('dropdownTrigger'));
});

If clicking the link updates the hash, the e.preventDefault(); is not working. Check, that the links really get bound.
Seems like they are not. Try replacing e.preventDefault(); with alert('I am bound!!!'); and see what happens upon clicking the link.
Are you wrapping the code in document ready?
EDIT: If I understand it correctly, your anchor click handler is redundant, as clicking the link updates the hash itself.

Related

window is not loading immediately after clicking a link

When I click an anchor link, the current page should be loaded again immediately:
<a href="javascript:void(0);" class="BackToFirst"
onclick="popNav('BackToFirst');">Back</a>
function popNav(type) {
if(type == "BackToFirst") {
$(".first").show();
$(".second").hide();
$('.BackToFirst').click(function() {
document.location.href = window.location.href;
});
}
}
I expect that when a user clicks on the link, the current page will load immediately but it is taking some time to load.
It is unclear what you are trying to do.
show/hide is immediately undone when you reload the page
it is recommended to use location.reload(1) instead of setting the supposedly read-only document.location
you might want to use e.preventDefault instead of the javascript void
Are you absolutely sure this is not an X/Y problem? Can you explain the actual usecase?
var current = sessionStorage.getItem("which"); // does not run in a snippet
current = current ? current.split("|") : []
if (current.length) {
$("." + current[0]).show();
$("." + current[1]).hide();
}
$(".BackToFirst").on("click", function(e) {
e.preventDefault();
$(".first").show();
$(".second").hide();
sessionStorage.setItem("which", "first|second")
setTimeout(function() {
location.reload(1); // are you absolutely sure this is not an X/Y problem?
}, 500); //let the show/hide sink in
});
Back
Your Click handler is only assigning a new click handler to the link, try this which just directly navigates:
function popNav(type){
if(type=="BackToFirst")
{
$(".first").show();
$(".second").hide();
document.location.href = window.location.href;
}
}
I think that you got two concepts mixed up, the above code attaches a DOM event to the link directly on it, the other way would be to use JQuery to attach an event to that button like so:
HTML:
Back
Script:
$('.BackToFirst').click(function(){
$(".first").show();
$(".second").hide();
document.location.href = window.location.href;
});
If you still want to check the type then data attributes are a nice way to go when working with JQuery:
Back
$('.BackToFirst').click(function(){
if($(this).data('linktype') == 'BackToFirst'){
$(".first").show();
$(".second").hide();
document.location.href = window.location.href;
}
});
I think you are overcomplicating your code. You are binding onClick after you click on the element. I think something like this should be better.
HTML:
Back
JS:
function onClickHandler(type){
if(type !== 'BackToFirst') {
return;
}
$(".first").show();
$(".second").hide();
location.reload();
}

Gracefully bubble up with a clicktarget

I am working with this plugin that runs off of the data attribute. Basically when you click anywhere on the body it will determine if the click target has this specific data-vzpop. The problem is lets say I have a div and inside the div is an a href. It only acknowledges the a href as the click target and not the div (which makes sense).
What I want to try and do in some cases is put the data attribute on the containing div that way anything within the div works on click.
Here is a sample of the issue with jsfiddle it requires viewing the console so you can actually see which element is registered as being clicked.
<div data-vzpop>
Click Me
</div>
$('body').on('click', function(evt){
var clickTarget = evt.target;
if ($(clickTarget).attr('data-vzpop') !== undefined){
evt.preventDefault();
console.log('called correctly')
} else {
console.log('not called correctly')
}
console.log(clickTarget)
});
fiddle
You would use Event delegation:
$('body').on('click', '[data-vzpop]', function(evt) {
This will only trigger when the evt.target has a data attribute of data-vzpop, no matter the value.
If you want items inside the [data-vzpop] to trigger it as well, you would use your original click event but check that the $(clickTarget).closest('[data-vzpop]').length > 0 to determine if it's a nested target.
$('body').on('click', function(evt){
var clickTarget = evt.target;
if ($(clickTarget).attr('data-vzpop') != null ||
$(clickTarget).closest('[data-vzpop]').length > 0){
evt.preventDefault();
console.log('called correctly')
} else {
console.log('not called correctly')
}
console.log(clickTarget)
});

Intercept clicks, make some calls then resume what the click should have done before

Good day all, I have this task to do:
there are many, many many webpages, with any kind of element inside, should be inputs, buttons, links, checkboxes and so on, some time there should be a javascript that could handle the element behaviour, sometimes it is a simple ... link.
i have made a little javascript that intercepts all the clicks on clickable elements:
$( document ).ready(function() {
$('input[type=button], input[type=submit], input[type=checkbox], button, a').bind('click', function(evt, check) {
if (typeof check == 'undefined'){
evt.preventDefault();
console.log("id:"+ evt.target.id+", class:"+evt.target.class+", name:"+evt.target.name);
console.log (check);
$(evt.target).trigger('click', 'check');
}
});
});
the logic is: when something is cllicked, I intercept it, preventDefault it, make my track calls and then resme the click by trigger an event with an additional parameter that will not trigger the track call again.
but this is not working so good. submit clicks seams to work, but for example clicking on a checkbox will check it, but then it cannot be unchecked, links are simply ignored, I track them (in console.log() ) but then the page stay there, nothing happens.
maybe I have guessed it in the wrong way... maybe i should make my track calls and then bind a return true with something like (//...track call...//).done(return true); or something...
anyone has some suggestions?
If you really wanted to wait with the click event until you finished with your tracking call, you could probably do something like this. Here's an example for a link, but should be the same for other elements. The click event in this example fires after 2seconds, but in your case link.click() would be in the done() method of the ajax object.
google
var handled = {};
$("#myl").on('click', function(e) {
var link = $(this)[0];
if(!handled[link['id']]) {
handled[link['id']] = true;
e.preventDefault();
e.stopPropagation();
//simulate async ajax call
window.setTimeout(function() {link.click();}, 2000);
} else {
//reset
handled[link['id']] = false;
}
});
EDIT
So, for your example, this would look something like this
var handled = {};
$( document ).ready(function() {
$('input[type=button], input[type=submit], input[type=checkbox], button, a').bind('click', function(evt) {
if(!handled[evt.target.id]) {
handled[evt.target.id] = true;
evt.preventDefault();
evt.stopPropagation();
$.ajax({
url: 'your URL',
data: {"id" : evt.target.id, "class": evt.target.class, "name": evt.target.name},
done: function() {
evt.target.click();
}
});
} else {
handled[evt.target.id] = false;
}
});

X-Editable: stop propagation on "click to edit"

I have an editable element inside a div which itself is clickable. Whenever I click the x-editable anchor element, the click bubbles up the DOM and triggers a click on the parent div. How can I prevent that? I know it's possible to stop this with jQuery's stopPropagation() but where would I call this method?
Here's the JSFiddle with the problem: http://jsfiddle.net/4RZvV/ . To replicate click on the editable values and you'll see that the containing div will catch a click event. This also happens when I click anywhere on the x-editable popup and I'd like to prevent that as well.
EDIT after lightswitch05 answer
I have multiple dynamic DIVs which should be selectable so I couldn't use a global variable. I added an attribute to the .editable-click anchors which get's changed instead.
editable-active is used to know if the popup is open or not
editable-activateable is used instead to know if that .editable-click anchor should be treated like it is
$(document).on('shown', "a.editable-click[editable-activateable]", function(e, reason) {
return $(this).attr("editable-active", true);
});
$(document).on('hidden', "a.editable-click[editable-activateable]", function(e, reason) {
return $(this).removeAttr("editable-active");
});
The check is pretty much like you've described it
$(document).on("click", ".version", function() {
$this = $(this)
// Check that the xeditable popup is not open
if($this.find("a[editable-active]").length === 0) { // means that editable popup is not open so we can do the stuff
// ... do stuff ...
}
})
For the click on the links, simply catch the click event and stop it:
$("a.editable-click").click(function(e){
e.stopPropagation();
});
The clicks within X-editable are a bit trickier. One way is to save a flag on weather the X-editable window is open or not, and only take action if X-editable is closed
var editableActive = false;
$("a.editable-click").on('shown', function(e, reason) {
editableActive = true;
});
$("a.editable-click").on('hidden', function(e, reason) {
editableActive = false;
});
$("div.version").click(function(e) {
var $this;
$this = $(this);
if(editableActive === false){
if ($this.hasClass("selected")) {
$(this).removeClass("selected");
} else {
$(this).addClass("selected");
}
}
});
Fixed Fiddle
It's not pretty, but we solved this problem with something like:
$('.some-class').click(function(event) {
if(event.target.tagName === "A" || event.target.tagName === "INPUT" || event.target.tagName === "BUTTON"){
return;
}
We're still looking for a solution that doesn't require a specific list of tagNames that are okay to click on.

jQuery Mobile: Swipeleft/Swiperight is jumping itself

i use this code to react on the swipeleft/swiperight event:
$('body').live('pagecreate', function(event) {
$('div[data-role="page"]').live("swipeleft", function() {
var nextpage = $(this).next('div[data-role="page"]');
// swipe using id of next page if exists
if (nextpage.length > 0) {
$.mobile.changePage(nextpage);
}
});
$('div[data-role="page"]').live("swiperight", function() {
var prevpage = $(this).prev('div[data-role="page"]');
// swipe using id of previous page if exists
if (prevpage.length > 0) {
$.mobile.changePage(prevpage, {
reverse : true,
});
}
});
});
It works, but after about 3 swipes (maybe when i reach the end of the 4 pages) there´s no normal behaviour anymore. For example: I swipe left --> i get the nextpage but then it swipes back and then again (i reach the expected page but not in that case i want). That happens after about 3 swipes all the time. What´s wrong with the code?
Thx a lot!
You know there is a plugin from the JQM devs just for that: JQM pagination
I think your problem is related to multiple bindings.
Put a console.log in every binding to see how often it fires. Like so:
$('body').live('pagecreate', function(event) {
console.log( "PAGECREATE fired")
$('div[data-role="page"]').live("swipeleft", function() {
console.log("binding to swipe-left on "+$(this).attr('id') );
var nextpage = $(this).next('div[data-role="page"]');
// swipe using id of next page if exists
if (nextpage.length > 0) {
$.mobile.changePage(nextpage);
}
});
$('div[data-role="page"]').live("swiperight", function() {
console.log("binding to swipe-right "+$(this).attr('id');
var prevpage = $(this).prev('div[data-role="page"]');
// swipe using id of previous page if exists
if (prevpage.length > 0) {
$.mobile.changePage(prevpage, {
reverse : true,
});
}
});
});
If these fire more than once, you will attach multiple bindings to your pages, which will all trigger changePage on swipe, when you only want one event to fire with every swipe.
EDIT:
First up, if you are using latest Jquery you should bind using on/off and not use live anymore.
One way would be to unbind on pagehide and re-bind when the page is reloaded. I guess that would be recommended way. However if you are not removing the page from the DOM when swiping to the next page, you will unbind and since pagecreate will not fire again (page still in the DOM, no need to create), you will not bind again when you swipe back.
I'm also dealing with this a lot and am using this:
$(document).on('pageshow', 'div:jqmData(role="page")', function(){
var page = $(this), nextpage, prevpage;
// check if the page being shown already has a binding
if ( page.jqmData('bound') != true ){
// if not, set blocker
page.jqmData('bound', true)
// bind
.on('swipeleft.paginate', function() {
console.log("binding to swipe-left on "+page.attr('id') );
nextpage = page.next('div[data-role="page"]');
if (nextpage.length > 0) {
$.mobile.changePage(nextpage);
}
})
.on('swiperight.paginate', function(){
console.log("binding to swipe-right "+page.attr('id');
prevpage = page.prev('div[data-role="page"]');
if (prevpage.length > 0) {
$.mobile.changePage(prevpage, {
reverse : true,
});
};
});
}
});
This will fire with every pageshow and check if the page is bound. If not, it sets the bindings on this page. The next time pageshow fires bound will be true, so it will not re-bind. If the page is removed from the DOM and reloaded, bound will not be set and the binding will be reset.
I have also added .paginate to your swipeleft/swiperight so you could remove them all at once using off

Categories

Resources