refactor this javascript code - javascript

I have two click-events, that are nearly similar, but not quite. I am wondering how to refactor them best:
$('.remove_fields.dynamic').live('click', function(e) {
var $this = $(this);
var after_removal_trigger_node = $this.closest(".nested-fields").parent();
trigger_removal_callback($this);
e.preventDefault();
$this.closest(".nested-fields").remove();
trigger_after_removal_callback(after_removal_trigger_node);
});
$('.remove_fields.existing').live('click', function(e) {
var $this = $(this);
var after_removal_trigger_node = $this.closest(".nested-fields").parent();
trigger_removal_callback($this);
e.preventDefault();
$this.prev("input[type=hidden]").val("1");
$this.closest(".nested-fields").hide();
trigger_after_removal_callback(after_removal_trigger_node);
});
As you can tell there is a fair bit of overlap. I am wondering what the best/nicest way would be to refactor this code.

Do a class check in the .remove_fields click function.
$('.remove_fields').click(function(e) {
var $this = $(this);
var after_removal_trigger_node = $this.closest(".nested-fields").parent();
trigger_removal_callback($this);
e.preventDefault();
if($this.hasClass("dynamic") {
$this.closest(".nested-fields").remove();
} else if($this.hasClass("existing")) {
$this.prev("input[type=hidden]").val("1");
$this.closest(".nested-fields").hide();
}
trigger_after_removal_callback(after_removal_trigger_node);
});

Combine the selectors into $('.remove_fields.dynamic, .remove_fields.existing').
Then, test if $this has class existing. If so, run $this.prev("input[type=hidden]").val("1");.
Done.

taking cleaning a bit more:
$('.remove_fields').click(function(e) {
e.preventDefault();
var $this = $(this);
var $nestedFields = $this.closest(".nested-fields");
trigger_removal_callback($this);
if($this.hasClass("dynamic") {
$nestedFields.remove();
} else if($this.hasClass("existing")) {
$this.prev("input[type=hidden]").val("1");
$nestedFields.hide();
}
trigger_after_removal_callback($nestedFields.parent());
});

Related

How would I loop this?

I am trying to loop functionality for buttons I've made. I have 3 video collections. and they all need the same script for next and previous.
What I want is that I only have to make one next button and one prev button.
This is what it should do:
$("#next-1").click(function(){
var $this = $(".video-album-1");
callback = function() {
$this.insertBefore($this.siblings(':eq(2)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
$("#prev-1").click(function(){
var $this = $(".video-album-1");
callback = function() {
$this.insertAfter($this.siblings(':eq(3)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
$("#next-2").click(function(){
var $this = $(".video-album-2");
callback = function() {
$this.insertBefore($this.siblings(':eq(2)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
$("#prev-2").click(function(){
var $this = $(".video-album-2");
callback = function() {
$this.insertAfter($this.siblings(':eq(3)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
I've already tried to loop it, but this doesn't work:
for (i = 0; i < 3; i++) {
$("#next-"+i).click(function(){
var $this = $(".video-album-"+i);
callback = function() {
$this.insertBefore($this.siblings(':eq(2)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
$("#prev-"+i).click(function(){
var $this = $(".video-album-"+i);
callback = function() {
$this.insertAfter($this.siblings(':eq(3)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
}
Is there anyone that could tell me a solution? I do not want to repeatedly create #next-3 #next-4 ids etc..
Thanks in advance.
Can you take a look at below approach:
$("[id^=next-]").click(function(){
var current_id = $(this).attr("id");
var number = current_id.replace("next-","");
var $this = $(".video-album-"+number);
callback = function() {
$this.insertBefore($this.siblings(':eq(2)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
$("[id^=prev-]").click(function(){
var current_id = $(this).attr("id");
var number = current_id.replace("prev-","");
var $this = $(".video-album-"+number);
callback = function() {
$this.insertAfter($this.siblings(':eq(3)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
Here we are using jQuery's starts with selector to attach the event to required DOM elements and within event handler we are grabbing the associated number for further process.
Use a class with next(),prev(),find() to get the video album class based on the next/prev elements or use this hack
$('[id^="next-"]').click(function(){
var i = $(this).attr('id').split('-')[1];
var $this = $(".video-album-"+i);
callback = function() {
$this.insertBefore($this.siblings(':eq(2)'));
}
$this.fadeOut(200, callback).fadeIn(400);
});
$('[id^="prev-"]').click(function(){
var i = $(this).attr('id').split('-')[1];
var $this = $(".video-album-"+i);
callback = function() {
$this.insertAfter($this.siblings(':eq(3)'));
}
$this.fadeOut(200, callback).fadeIn(400);
I'll emphasize now this is just one way, there might be better ones
Assuming these are your buttons
<button class="next" data-videocollectionname="video1">next</button>
<button class="next" data-videocollectionname="video2">next</button>
<button class="next" data-videocollectionname="video3">next</button>
<button class="previous" data-videocollectionname="video1">previous</button>
<button class="previous" data-videocollectionname="video2">previous</button>
<button class="previous" data-videocollectionname="video3">previous</button>
I've given each button a data attribute to say which collection the button is relevant too.
Forgive me if I've miss understood the question
$(".next").click(function(){
var videoCollectionName = $(this).data("videocollectionname");
var videoElement = $("#"+videoCollectionName);
//do whatever code you want for the videocollection
});
$(".previous").click(function(){
var videoCollectionName = $(this).data("videocollectionname");
var videoElement = $("#"+videoCollectionName);
//do whatever code you want for the videocollection
});
This question and its answers explain why your attempt didn't work correctly.
Based on your attempted for loop, this will work:
$("[id^=next-]").click(function() {
var i = this.id.substring(5);
var album = $(".video-album-" + i);
callback = function() {
album.insertBefore(album.siblings(':eq(2)'));
};
album.fadeOut(200, callback).fadeIn(400);
});
$("[id^=prev-]").click(function() {
var i = this.id.substring(5);
var album = $(".video-album-" + i);
callback = function() {
album.insertAfter(album.siblings(':eq(3)'));
};
album.fadeOut(200, callback).fadeIn(400);
});
That works by using an attribute starts with selector to match all of your next- (and prev-) buttons, then figures out the index from the id of the button actually clicked.
But there's probably a better answer we could give you if you quote your HTML.

Hide link title attribute based on screen width – won't unhide

I've made an attempt here based on some things I have gleaned, but this is just plain removing it altogether; the title doesn't return back at <= 768px
<script>
if( $(window).width() > 767) {
$('[title]').each( function() {
var $this = $(this);
$this.data('title',$this.attr('title'));
$this.removeAttr('title');
});
}
</script>
See http://jsfiddle.net/2nHxV/
if ($(window).width() > 767) {
$('[title]').each( function() {
var $this = $(this);
$this.data('title',$this.attr('title'));
$this.removeAttr('title');
});
} else {
// as in above `title` attribute removed and `data-title` added, so now you've
// to loop with data-title
$('[data-title]').each( function() {
var $this = $(this);
$this.data('title',$this.data('title'));
$this.removeAttr('data-title');
});
}
So put it back?
var $titles = [];
if( $(window).width()> 767) {
$('[title]').each( function() {
var $this = $(this);
$this.data('title',$this.attr('title'));
$this.removeAttr('title');
$titles.push($this);
});
} else {
$.each($titles, function(index, $this) {
$this.attr('title',$this.data('title'));
});
}
Working demo: http://jsfiddle.net/z3rr9d04/
You also might want to put this logic inside a $(window).on('resize', ...); handler since it'll only be executed once on page load as it stands currently.

Creating a re-usable function in JavaScript

I'm extracting a part of the current button ID and want it to re-use this in several other functions.
How could I make the following section common,
var idRec = $(this).attr("id"),
inp = idRec.substr(4,this.id.length);
So that it could be used in multiple .on('click',) events. Please see the following code and advise,
$(function() {
function studentsRecords(e) {
//do_somehting
}
$(document).on('click', '.studentID', function(e) {
var idRec = $(this).attr("id"),
inp = idRec.substr(2, this.id.length);
//do_something_using_inp
}).on('click', '.admissionID', function(e) {
//do_something_else_using_inp
});
});
$(function() {
function studentsRecords(e) {
//do_somehting
}
function get_id(that){
var idRec = that.id;
var inp = idRec.substr(2,that.id.length);
return inp
}
$(document)
.on('click', '.studentID', function(e) {
var inp = get_id(this);
//do_something_using_inp
})
.on('click', '.admissionID', function(e) {
var inp = get_id(this);
//do_something_else_using_inp
})
})
You can declare inp before click function. http://fiddle.jshell.net/x3xo25vz/

jQuery click inside each loop

I want to loop my click events, to make the code shorter. I might have 30 of these values later on.
My working code
$(document).ready(function () {
var last_click = '';
$("#title").click(function() { last_click = 'title'; });
$("#subtitle").click(function() { last_click = 'subtitle'; });
$("#test").click(function() { last_click = 'test'; });
});
This is how I want it (not working)
My guess is that the each-loop runs on dom ready and then never again and that way the click-event can never be triggered?
$(document).ready(function () {
var last_click = '';
var contents = new Array();
contents = ['title', 'subtitle', 'test'];
$.each(contents , function(index, value){
$("#" + value).click(function() { last_click = value; });
});
});
If there is not solved like I would, I would be thankful for a nice workaround.
I would rather add a class to all elements you want to bind this to, eg class="last-click"
and define the binding once as:
$(".last-click").on('click', function() {
last_click = this.id;
}
If you really wanted to make it shorter, give them all a similar class.
$(document).ready(function () {
var last_click = '';
$(".theclass").click(function() {
last_click = this.id;
});
});
if you have value attribute for your buttons or elements, you can do it:
$(document).ready(function() {
var last_click = '';
$("input").click(function() {
last_click = $(this).attr('value');
alert(last_click);
});
});​
I assumed that you are using "input type="button". Also here is the demo you can see it in action: http://jsfiddle.net/rS2Gb/5/

Toggling two events on one button

I'm trying to add some functionality to be able to edit comments inline. So far it's pretty close, but I'm experiencing issues trying to trigger a second event. It works the first time, but after that, fails.
$(function() {
var $editBtn = $('.js-edit-comment-btn');
var clicked = false;
$editBtn.on('click', $editBtn, function() {
clicked = true;
var $that = $(this);
var $form = $that.closest('.js-edit-comment');
var $commentTextBody = $that.closest('div').find('.js-comment-body');
var commentText = $commentTextBody.text();
var $editableText = $('<textarea />');
if ($that.text() === 'Save Edits') {
$that.text('Saving...').attr('disabled', true);
} else {
$that.text('Save Edits').attr('alt', 'Save your edits');
}
// Replace div with textarea, and populate it with the comment text
var makeDivTextarea = function($editableText, commentText, $commentTextBody) {
$editableText.val(commentText);
$commentTextBody.replaceWith($editableText);
$editableText.addClass('gray_textarea js-edited-comment').width('100%').css('padding', '4px').focus();
};
makeDivTextarea($editableText, commentText, $commentTextBody);
var saveEdits = function($that, $editableText) {
$that.on('click', $that, function() {
if (clicked) {
var comment = $that.closest('div').find('.js-edited-comment').val();
$editableText.wrap('<div class="js-comment-body" />').replaceWith(comment);
$that.text('Edit').attr('alt', 'Edit Your Comment').attr('disabled', false);
$('#output').append('saved');
clicked = false;
return false;
}
});
};
saveEdits($that, $editableText);
return false;
});
});​
jsfiddle demo here
Hiya demo for your working solution: http://jsfiddle.net/8P6uz/
clicked=true was the issue :)) I have rectified another small thing. i.e. $('#output') is set to empty before appending another saved hence text **saved** will only appear once.
small note: If I may suggest use Id of the button or if there are many edit buttons try using this which you already i reckon; I will see if I can write this more cleaner but that will be sometime latter-ish but this should fix your issue. :) enjoy!
Jquery Code
$(function() {
var $editBtn = $('.js-edit-comment-btn');
var clicked = false;
$editBtn.on('click', $editBtn, function() {
clicked = true;
var $that = $(this);
var $form = $that.closest('.js-edit-comment');
var $commentTextBody = $that.closest('div').find('.js-comment-body');
var commentText = $commentTextBody.text();
var $editableText = $('<textarea />');
if ($that.text() === 'Save Edits') {
$that.text('Saving...').attr('disabled', true);
} else {
$that.text('Save Edits').attr('alt', 'Save your edits');
}
// Replace div with textarea, and populate it with the comment text
var makeDivTextarea = function($editableText, commentText, $commentTextBody) {
$editableText.val(commentText);
$commentTextBody.replaceWith($editableText);
$editableText.addClass('gray_textarea js-edited-comment').width('100%').css('padding', '4px').focus();
};
makeDivTextarea($editableText, commentText, $commentTextBody);
var saveEdits = function($that, $editableText) {
$that.on('click', $that, function() {
if (clicked) {
var comment = $that.closest('div').find('.js-edited-comment').val();
$editableText.wrap('<div class="js-comment-body" />').replaceWith(comment);
$that.text('Edit').attr('alt', 'Edit Your Comment').attr('disabled', false);
$('#output').text("");
$('#output').append('saved');
clicked = true;
return false;
}
});
};
saveEdits($that, $editableText);
return false;
});
});​

Categories