How can I refactor this jQuery code? - javascript

The code below is for a simple newsletter signup widget.
I'm sure there's a way to make it more concise, any ideas?
var email_form = $('.widget_subscribe form');
var email_submit = $('.widget_subscribe .submit');
var email_link = $('.widget_subscribe .email');
// Hide the email entry form when the page loads
email_form.hide();
// Show the form when the email link is clicked
$(email_link).click( function () {
$(this).toggle();
$(email_form).toggle();
return false;
});
// Hide the form when the form submit is clicked
$(email_submit).click( function () {
$(email_link).toggle();
$(email_form).toggle();
});
// Clear/reset the email input on focus
$('input[name="email"]').focus( function () {
$(this).val("");
}).blur( function () {
if ($(this).val() == "") {
$(this).val($(this)[0].defaultValue);
}
});

You have some similar code here.
// Show the form when the email link is clicked
$(email_link).click( function () {
$(this).toggle();
$(email_form).toggle();
return false;
});
// Hide the form when the form submit is clicked
$(email_submit).click( function () {
$(email_link).toggle();
$(email_form).toggle();
});
It could be refactored so the similarity is obvious.
// Show the form when the email link is clicked
$(email_link).click( function () {
$(email_link).toggle();
$(email_form).toggle();
return false;
});
// Hide the form when the form submit is clicked
$(email_submit).click( function () {
$(email_link).toggle();
$(email_form).toggle();
});
So you could wrap toggling the link and the form into a function.
var toggleEmailLinkAndForm = function () {
$(email_link).toggle();
$(email_form).toggle();
}
$(email_link).click(toggleEmailLinkAndForm);
$(email_submit).click(toggleEmailLinkAndForm);
And as others have pointed out, you can drop the redunant $()s.
var toggleEmailLinkAndForm = function () {
email_link.toggle();
email_form.toggle();
}
email_link.click(toggleEmailLinkAndForm);
email_submit.click(toggleEmailLinkAndForm);

It's already pretty concise, there's not much more you can do.
Anywhere you have $(email_submit) you can just have email_submit, because you've already wrapped it in $() (which makes it a jquery object).
Eg:
email_submit.click( function () {
email_link.toggle();
email_form.toggle();
});

I like Patrick McElhaney Code Best.
toggleEmailLinkAndForm() {
email_link.toggle();
email_form.toggle();
}
email_link.click(toggleEmailLinkAndForm);
email_submit.click(toggleEmailLinkAndForm);
Part of refactoring is not going overboard. I would not recommend creating an extra click event that calls the other click event. The point of refactoring is readability and flexibility. You can also use the jQuery method "add" to shrink the code but it will become even harder to read.
email_link.add(email_submit).click(function(){
email_link.add(email_form).toggle();
});

Like Patrick said (+1), and you can also skip the extra function:
email_submit.click(function () {
email_link.toggle();
email_form.toggle();
});
email_link.click(function () {
email_submit.click(); //calls the click function already subscribed
return false;
});

Related

Password Hide/Show Button only works one-way

I've build a little function which should change the content of a span, which lies in a <td> together with a button to the value of the button on the first push of the button. When the button is pushed again, it should replace the content with four asterisks.
Unfortunately, it only works one-way
$(".pw_show").click(function () {
$(this).parent('td').children('span').html($(this).attr('value'));
$(this).toggleClass('pw_hide pw_show');
});
$(".pw_hide").click(function () {
$(this).parent('td').children('span').html("****");
$(this).toggleClass('pw_hide pw_show');
});
Fiddle down here: http://jsfiddle.net/kXNk8/218/
You toggle the classes so you need to use dynamic event binding with .on() instead of .click():
$('table').on('click',".pw_show", function () {
$(this).parent('td').children('span').html($(this).attr('value'));
$(this).toggleClass('pw_hide pw_show');
});
$('table').on('click',".pw_hide", function () {
$(this).parent('td').children('span').html("****");
$(this).toggleClass('pw_hide pw_show');
});
jsFiddle example
After the first click, .pw_show no longer exists on the element which causes your issue.
Delegating to a static element using .on() will make it work or you should change it totally.
$(document).on("click", ".pw_show", function () {
$(this).parent('td').children('span').html($(this).attr('value'));
$(this).toggleClass('pw_hide pw_show');
});
$(document).on("click", ".pw_hide", function () {
$(this).parent('td').children('span').html("****");
$(this).toggleClass('pw_hide pw_show');
});
Fiddle: http://jsfiddle.net/4atrmks3/
You should really just toggle the attribute for type between password and text not use a placeholder.
var password = false;
$('a').click(function(){
if (!password){
password = true;
$('input').attr('type', 'password');
} else {
password = false;
$('input').attr('type', 'text');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text">
Click to toggle password / text

How to change button appearance in a jquery call

When I click on a link in my jsp
<a id="ajout_lien" href="javascript:submitFormAjout()" class="under">go </a>
I call a javascript function
function submitFormAjout() {
document.forms['constitutionForm'].submit();
}
Then there is the jquery call to prevent from a double submit :
$("form").submit(function () {
if ($(this).valid()) {
$(this).submit(function () {
return false;
});
return true;
}
else {
return false;
}
});
How can I change the appearance of my link while the submit is done (the real one, in the Jquery's call)? By instance change the color and label of my tag ( let's say from "go" to "waiting"?
I want it to be as generic as possible : if, in another jsp of my application, the submit is made on a button, not a link, I an use 3 parameters : newClass (css) , idElement (of the button or the link) and label if it's a link.
Try the below code
function submitFormAjout() {
document.getElementById("ajout_lien").className ="over";
document.forms['constitutionForm'].submit();
}
jQuery .one() ensures submit event is triggered only once.
$("form").one("submit", function () {
if ($(this).valid()) {
$(this).submit(function () {
return false;
});
return true;
}
else {
return false;
}
});
JQuery:
function submitFormAjout() {
$("#"+ (this).attr("id")).addClass("over");
$('form[name="constitutionForm"]').submit();
}
HTML:
<a id="ajout_lien" href="javascript:submitFormAjout()" class="under">text </a>
If you want className to be dynamic, then you can pass it as function parameter and use that value in the method.

How to close div when div loses focus?

I made a simple plunkr here http://plnkr.co/edit/zNb65ErYH5HXgAQPOSM0?p=preview
I created a little datepicker I would like this to close itself when you focus out of it (focusout of datepicker) if I put blur on input I'm unable to use the datepicker, if I put focusout event on datepicker it doesn't works
I also tried:
angular.element(theCalendar).bind('blur', function () {
$scope.hideCalendar();
});
but it doesn't work.
Any clue?
this is because you are removing the item before you get a chance to do anything, here is a working example:
http://plnkr.co/edit/mDfV9NLAQCP4l7wHdlfi?p=preview
just add a timeout:
thisInput.bind('blur', function () {
$timeout(function(){
$scope.hideCalendar();
}, 200);
});
have you considered using existing datepickers? like angularUI or angular-strap: http://mgcrea.github.io/angular-strap/##datepickers
Update:
Not a complete solution, but should get you quite closer:
angular.element($document[0].body).bind('click', function(e){
console.log(angular.element(e.target), e.target.nodeName)
var classNamed = angular.element(e.target).attr('class');
var inThing = (classNamed.indexOf('datepicker-calendar') > -1);
if (inThing || e.target.nodeName === "INPUT") {
console.log('in');
} else {
console.log('out');
$timeout(function(){
$scope.hideCalendar();
}, 200);
}
});
http://plnkr.co/edit/EbQl5xsCnG837rAEhBZh?p=preview
What you want to do then is to listen for a click on the page, and if the click is outside of the calendar, then close it, otherwise do nothing. The above only takes into account that you are clicking on something that has a class name which includes datepicker-calendar, you will need to adjust it so that clicking within the calendar doesn't close it as well.
How about closing on mouseout?
You need to cancel the close if you move to another div in the calendar though:
//get the calendar as element
theCalendar = element[0].children[1];
// hide the calendar on mouseout
var closeCalendarTimeout = null;
angular.element(theCalendar).bind('mouseout', function () {
if ( closeCalendarTimeout !== null )
$timeout.cancel(closeCalendarTimeout);
closeCalendarTimeout = $timeout(function () {
$scope.hideCalendar();
},250)
});
angular.element(theCalendar).bind('mouseover', function () {
if ( closeCalendarTimeout === null ) return
$timeout.cancel(closeCalendarTimeout);
closeCalendarTimeout = null;
});
EDIT
Adding a tabindex attribute to a div causes it to fire focus and blur events.
, htmlTemplate = '<div class="datepicker-calendar" tabindex="0">' +
angular.element(theCalendar).bind('blur', function () {
$scope.hideCalendar();
});
So, i know it probably is not the best practice or the best way to do this, but at the end i fixed and got what i need using this:
thisInput.bind('focus click', function bindingFunction() {
isMouseOnInput = true;
$scope.showCalendar();
angular.element(theCalendar).triggerHandler('focus');
});
thisInput.bind('blur focusout', function bindingFunction() {
isMouseOnInput = false;
});
angular.element(theCalendar).bind('mouseenter', function () {
isMouseOn = true;
});
angular.element(theCalendar).bind('mouseleave', function () {
isMouseOn = false;
});
angular.element($window).bind('click', function () {
if (!isMouseOn && !isMouseOnInput) {
$scope.hideCalendar();
}
});
I setted up some boolean vars to check where mouse is when you click the page and it works like a charm if you have some better solution that works , please let me know, but this actually fixed all.
I accept this as the answer but i thank all the guys on this page!

Prevent click after focus event

When user clicks on input field, two consecutive events are being executed: focus and click.
focus always gets executed first and shows the notice. But click which runs immediately after focus hides the notice. I only have this problem when input field is not focused and both events get executed consecutively.
I'm looking for the clean solution which can help me to implement such functionality (without any timeouts or weird hacks).
HTML:
<label for="example">Example input: </label>
<input type="text" id="example" name="example" />
<p id="notice" class="hide">This text could show when focus, hide when blur and toggle show/hide when click.</p>
JavaScript:
$('#example').on('focus', _onFocus)
.on('blur', _onBlur)
.on('click', _onClick);
function _onFocus(e) {
console.log('focus');
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
$('#notice').removeClass('hide');
}
function _onClick(e) {
console.log('click');
$('#notice').toggleClass('hide');
}
function _onBlur(e) {
console.log('blur');
$('#notice').addClass('hide');
}
UPDATED Fiddle is here:
I think you jumbled up the toggles. No need to prevent propagation and all that. Just check if the notice is already visible when click fires.
Demo: http://jsfiddle.net/3Bev4/13/
Code:
var $notice = $('#notice'); // cache the notice
function _onFocus(e) {
console.log('focus');
$notice.removeClass('hide'); // on focus show it
}
function _onClick(e) {
console.log('click');
if ($notice.is('hidden')) { // on click check if already visible
$notice.removeClass('hide'); // if not then show it
}
}
function _onBlur(e) {
console.log('blur');
$notice.addClass('hide'); // on blur hide it
}
Hope that helps.
Update: based on OP's clarification on click toggling:
Just cache the focus event in a state variable and then based on the state either show the notice or toggle the class.
Demo 2: http://jsfiddle.net/3Bev4/19/
Updated code:
var $notice = $('#notice'), isfocus = false;
function _onFocus(e) {
isFocus = true; // cache the state of focus
$notice.removeClass('hide');
}
function _onClick(e) {
if (isFocus) { // if focus was fired, show/hide based on visibility
if ($notice.is('hidden')) { $notice.removeClass('hide'); }
isFocus = false; // reset the cached state for future
} else {
$notice.toggleClass('hide'); // toggle if there is only click while focussed
}
}
Update 2: based on OP's observation on first click after tab focus:
On second thought, can you just bind the mousedown or mouseup instead of click? That will not fire the focus.
Demo 3: http://jsfiddle.net/3Bev4/24/
Updated code:
$('#example').on('focus', _onFocus)
.on('blur', _onBlur)
.on('mousedown', _onClick);
var $notice = $('#notice');
function _onFocus(e) { $notice.removeClass('hide'); }
function _onClick(e) { $notice.toggleClass('hide'); }
function _onBlur(e) { $notice.addClass('hide'); }
Does that work for you?
Setting a variable for "focus" seems to do the trick : http://jsfiddle.net/3Bev4/9/
Javascript:
$('#example').on('focus', _onFocus)
.on('click', _onClick)
.on('blur', _onBlur);
focus = false;
function _onFocus(e) {
console.log('focus');
$('#notice').removeClass('hide');
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
focus = true;
}
function _onClick(e) {
console.log('click');
if (!focus) {
$('#notice').toggleClass('hide');
} else {
focus = false;
}
}
function _onBlur(e) {
console.log('blur');
$('#notice').addClass('hide');
}
If you want to hide the notice onBlur, surely it needs to be:
function _onBlur(e) {
console.log('blur');
$('#notice').addClass('hide'); // Add the hidden class, not remove it
}
When doing this in the fiddle, it seemed to fix it.
The code you have written is correct, except that you have to replae $('#notice').removeClass('hide'); with $('#notice').addClass('hide');
Because onBlur you want to hide so add hide class, instead you are removing the "hide" calss.
I hope this is what the mistake you have done.
Correct if I am wrong, Because I don't know JQuery much, I just know JavaScript.
you can use many jQuery methods rather than add or move class:
Update: add a params to deal with the click function
http://jsfiddle.net/3Bev4/23/
var showNotice = false;
$('#example').focus(function(){
$('#notice').show();
showNotice = true;
}).click(function(){
if(showNotice){
$('#notice').show();
showNotice = false;
}else{
showNotice = true;
$('#notice').hide();
}
}).blur(function(){
$('#notice').hide();
});

prevent double clicks on links with jQuery

What's the best way to prevent a double-click on a link with jQuery?
I have a link that triggers an ajax call and when that ajax call returns it shows a message.
The problem is if I double-click, or click it twice before the ajax call returns, I wind up with two messages on the page when I really want just one.
I need like a disabled attribute on a button. But that doesn't work on links.
$('a').on('click', function(e){
e.preventDefault();
//do ajax call
});
You can use data- attributes, something like this:
$('a').on('click', function () {
var $this = $(this);
var alreadyClicked = $this.data('clicked');
if (alreadyClicked) {
return false;
}
$this.data('clicked', true);
$.ajax({
//some options
success: function (data) { //or complete
//stuff
$this.data('clicked', false);
}
})
});
I came with next simple jquery plugin:
(function($) {
$.fn.oneclick = function() {
$(this).one('click', function() {
$(this).click(function() { return false; });
});
};
// auto discover one-click elements
$(function() { $('[data-oneclick]').oneclick(); });
}(jQuery || Zepto));
// Then apply to selected elements
$('a.oneclick').oneclick();
Or just add custom data atribute in html:
<a data-oneclick href="/">One click</a>
You need async:false
By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false.
$.ajax({
async: false,
success: function (data) {
//your message here
}
})
you can use a dummy class for this.
$('a#anchorID').bind('click',function(){
if($(this).hasClass('alreadyClicked')){
return false;
}else{
$(this).addClass('alreadyClicked);
$/ajax({
success: function(){$('a#anchorID').removeClass('alreadyClicked');},
error: function(){$('a#anchorID').removeClass('alreadyClicked');}
});
}});
Check this example. You can disable the button via CSS attribute after the first click (and remove this attribute after an ajax request or with a setTimeout) or use the jQuery.one() function to remove the trigger after the first click (without disabling the button)
var normal_button = $('#normal'),
one_button = $('#one'),
disabled_button = $('#disabled'),
result = $('#result');
normal_button.on('click', function () {
result.removeClass('hide').append(normal_button.html()+'<br/>');
});
one_button.one('click', function () {
result.removeClass('hide').append(one_button.html()+'<br/>');
});
disabled_button.on('click', function () {
disabled_button.attr('disabled', true);
setTimeout(function () {
result.removeClass('hide').append(disabled_button.html()+'<br/>');
}, 2000);
});
Although there are some good solutions offered, the method I ended up using was to just use a <button class="link"> that I can set the disabled attribute on.
Sometimes simplest solution is best.
You can disable click event on that link second time by using Jquery
$(this).unbind('click');
See this jsfiddle for reference
Demo
You can disable your links (for instance, href="#" ), and use a click event instead, binded to the link using the jQuery one() function.
Bind all the links with class "button" and try this:
$("a.button").click(function() { $(this).attr("disabled", "disabled"); });
$(document).click(function(evt) {
if ($(evt.target).is("a[disabled]"))
return false;
});

Categories