How to start optimising/refactoring jQuery code - javascript

I have following jQuery code
$('a.editpo, a.resetpass').click(function(event){
event.preventDefault();
var urlToCall = $(this).attr('href');
var hyperlinkid = '#'+$(this).attr('id');
var targetId = $(this).attr('id').match(/\d+/);
var targetTrDiv = '#poformloadertr_'+targetId;
var targetTdDiv = '#poformloadertd_'+targetId;
var currentLink = $(this).html();
/*Todo: refactor or shorten the following statement*/
if((currentLink=='Edit' && $('#resetuserpassform_'+targetId).is(':visible'))
||
(currentLink=='Reset Pass' && $('#account-home-container-'+targetId).is(':visible'))
||
($(targetTdDiv).html() =='')
){
$.ajax({
url:urlToCall,
success: function(html){
$(targetTdDiv).html(html);
$(targetTrDiv).show();
}
});
}else{
$(targetTrDiv).hide();
$(targetTdDiv).html('');
}
});
The editpo and resetpass are classes applied on hyperlinks in column of table, namely Edit and Reset Pass, clicking these load up the form in a table row, ids for the respective tr and td is targetTrDiv and targetTdDiv. I am not good at JS and specially jQuery, but would still be interested in any optimisations.
But I am especially interested in reducing the condition statement.

First, you can optimize the following code:
var urlToCall = $(this).attr('href');
var hyperlinkid = '#'+$(this).attr('id');
var targetId = $(this).attr('id').match(/\d+/);
var targetTrDiv = '#poformloadertr_'+targetId;
var targetTdDiv = '#poformloadertd_'+targetId;
var currentLink = $(this).html();
to:
var wrappedSet$ = $(this);
var urlToCall = wrappedSet$.attr('href');
var hyperlinkid = '#'+wrappedSet$.attr('id');
var targetId = wrappedSet$.attr('id').match(/\d+/);
var targetTrDiv = '#poformloadertr_'+targetId;
var targetTdDiv = '#poformloadertd_'+targetId;
var currentLink = wrappedSet$.html();
EDIT: Also, you could remove the currentLink=='Edit' && and currentLink=='Reset Pass' && code snippets, because you can be sure that the right links were clicked using the class selector that you are using in your jQuery click handler (a.editpo, a.resetpass).
If you do that the codition statement will stays like this:
if(($('#resetuserpassform_'+targetId).is(':visible'))
||
($('#account-home-container-'+targetId).is(':visible'))
||
($(targetTdDiv).html() =='')
){
/* AJAX call goes here */
}
Besides, and hopefully I'm wrong, it's very unlikely that the condition statement can be more optimized. The reason for concluding this is that you want to much specificity that is likely impossible to achieve in other way.
Hope it helps you.

Related

jQuery function does not work on mouseover and click state

Building a new site and trying to reuse a JavaScript from the current site that change image on hover. I have a hard time to understand JavaScript so I hope someone can help me to see what I'm missing or doing wrong.
The current site (where I copied from) has many more script running and I don't know if some other script is missing. I have tried to change the initializing part of the script to fit the new site. Please have a look at the puzzlepiece on the current page. http://www.collyfiltreringsteknik.se.
I have got the script running to a point where it adds the four "quad" divs but the mouseover and click function does not work.
This is the html code
<div id="companyvalues" class="clearfix">
<div class="valueimage"></div>
</div>
The jQuery part
(function($){
$.fn.extend({
companyvalues: function(kwargs) {
var defaults = {
static_base:''
};
var options = $.extend(defaults, kwargs);
return this.each(function() {
var obj = $(this);
var div = $('div', obj);
var topleft = $('<div class="quad topleft" data-image="i0000" data-href="/grundpelare/tillganglighet/"></div>');
var topright = $('<div class="quad topright" data-image="i0003" data-href="/grundpelare/konkurrenskraft/"></div>');
var bottomleft = $('<div class="quad bottomleft" data-image="i0002" data-href="/grundpelare/miljoforbattring/"></div>');
var bottomright = $('<div class="quad bottomright" data-image="i0001" data-href="/grundpelare/kostnadsreducering/"></div>');
obj.prepend(bottomright);
obj.prepend(bottomleft);
obj.prepend(topright);
obj.prepend(topleft);
var divs = $('div.quad', obj);
$([ '/uploads/pages/varden_0000_yellow.png',
'/uploads/pages/varden_0001_red.png',
'/uploads/pages/varden_0002_green.png',
'/uploads/pages/varden_0003_blue.png'
]).preload({base_url:options.static_base});
divs.mouseover(function(event) {
var item = $(this);
var data_image = item.attr('data-image');
div.addClass(data_image);
event.preventDefault();
event.stopPropagation();
});
divs.mouseout(function(event) {
var item = $(this);
div.removeClass('i0000');
div.removeClass('i0001');
div.removeClass('i0002');
div.removeClass('i0003');
event.preventDefault();
event.stopPropagation();
});
divs.click(function(event) {
var item = $(this);
var href = item.attr('data-href')
document.location.href=href;
event.preventDefault();
event.stopPropagation();
});
});
}
});
})(jQuery);
Initializing the script.
$(document).ready(function() {
$('#companyvalues').companyvalues();
});

Inline Edit with jQuery UI date picker

I am trying to implement inline edit for the date field (use jQuery UI date picker)
The code I am using is as below;
$(document).on("click",".editableDateTxt", function () {
var currElmModelId = $(this).attr('data-model-id');
var currElmModelAttr = $(this).attr('data-model-attr');
var input = $('<input />', {'type': 'text','name':currElmModelAttr, 'style':'width:100px' ,'data-model-id': currElmModelId, 'data-model-attr': currElmModelAttr, 'class':'datePicker', 'value': $(this).html()});
var parent = $(this).parent();
parent.append(input);
$(this).remove();
input.datepicker().focus();
});
$(document).on("change", ".datePicker", function () {
//alert($(this).val());
var dataValid = $(this).attr('data-valid');
if (dataValid == "Y") {
var currElmModelId = $(this).attr('data-model-id');
var currElmModelAttr = $(this).attr('data-model-attr');
var divEle = $('<div />', {'class': 'editableDateTxt','name':currElmModelAttr, 'data-model-attr':currElmModelAttr,'data-model-id':currElmModelId,'html':$(this).val()});
var parent = $(this).parent();
parent.append(divEle);
$(this).remove();
}
});
$(document).on("blur",".datePicker", function () {
if (this.hasAttribute('data-model-id')) {
var dataValid = $(this).attr('data-valid');
if (typeof dataValid == "undefined" || dataValid == "Y") {
var currElmModelId = $(this).attr('data-model-id');
var currElmModelAttr = $(this).attr('data-model-attr');
var divEle = $('<div />', {'class': 'editableDateTxt','name':currElmModelAttr, 'data-model-attr':currElmModelAttr,'data-model-id':currElmModelId,'html':$(this).val()});
var parent = $(this).parent();
parent.append(divEle);
$(this).remove();
return false;
}
}
});
Now when I select any date or do blur, I get the following error;
Missing instance data for this datepicker
I think the issue is related to the jQuery UI datepicker using data()
So I have tried using detach() instead of remove()...
So I just used $(this).detach();
Could you guide me on the correct way of using detach() which might fix the issue...
You are probably having the same problem I was having. The code in your onblur is firing before the datepicker is completing its work so you are losing the input object before datepicker is done with it.
I was able to resolve this with one of the suggestions in the accepted answer on this question:
https://stackoverflow.com/a/1814308/3434790
The solution that worked in my scenario was to wrap my code inside my on blur function in a setTimeout which effectively made it so that code ran after the datepicker was done doing its thing.
In your case I would suggest the following:
$(document).on("blur",".datePicker", function () {
setTimeout(function(){
if (this.hasAttribute('data-model-id')) {
var dataValid = $(this).attr('data-valid');
if (typeof dataValid == "undefined" || dataValid == "Y") {
var currElmModelId = $(this).attr('data-model-id');
var currElmModelAttr = $(this).attr('data-model-attr');
var divEle = $('<div />', {'class': 'editableDateTxt','name':currElmModelAttr, 'data-model-attr':currElmModelAttr,'data-model-id':currElmModelId,'html':$(this).val()});
var parent = $(this).parent();
parent.append(divEle);
$(this).remove();
return false;
}
}
},100);
});

How to call javascript function only once during window.onscroll event?

function getH4() {
var xyz = document.getElementsByClassName('bucket_left');
for(var i=0;i<xyz.length;i++){
var x=document.getElementsByTagName("h4")[i].innerHTML;
var current_bucket = xyz[i];
var y=current_bucket.firstChild.href;
var newdiv = document.createElement('div');
newdiv.innerHTML = ""+x+"";
newdiv.className = "hover_title_h4";
current_bucket.appendChild(newdiv);
}
}
window.onscroll=getH4;
In above code i want to append new div in set of divs having class bucket_left and this divs generated from infinite scrolling. above code is working fine but on scroll it appends so many divs.
so how do i append only once ?
Add the following line at the end of your function:
function getH4() {
// ...
window.onscroll = null;
}
create a global boolean variable and set it to false. again set it to true in the window scroll event and chk the variable is false using a if block. put your code inside that if block.
var isScrolled = false;
function getH4() {
if(!isScrolled){
//your code
}
isScrolled = true
}
You only have to set the onscroll property to none as following at the end of you JavaScript function:
window.onscroll = null;
Now when the script executes for the first time, it will perform its function and the above line will set the onscroll to null and thus will not invoke any event on scroll of your mouse and so your function wont be invoked again and again on the event except for the first time.
Or you could handle it logically by setting a public var say var check = 0 and then set the variable to 1 when entered for the first time. So you need to check the value of check and based on that execute the function
var check = 1;
function getH4() {
if(check==1)
{
var xyz = document.getElementsByClassName('bucket_left');
for(var i=0;i<xyz.length;i++){
var x=document.getElementsByTagName("h4")[i].innerHTML;
var current_bucket = xyz[i];
var y=current_bucket.firstChild.href;
var newdiv = document.createElement('div');
newdiv.innerHTML = ""+x+"";
newdiv.className = "hover_title_h4";
current_bucket.appendChild(newdiv);
}
check=0;
}
}
you can try this:
when scrolling,the check equal false, and the append event will happen just once;
when the scroll end(mouseup or mouseout), the check equal true, you can append again.
var check = true;
function getH4(event) {
event.target.onmouseup = function() {
check = true;
}
event.target.onmouseout = function() {
check = true;
}
if (check) {
var xyz = document.getElementsByClassName('bucket_left');
for(var i=0;i<xyz.length;i++){
var x=document.getElementsByTagName("h4")[i].innerHTML;
var current_bucket = xyz[i];
var y=current_bucket.firstChild.href;
var newdiv = document.createElement('div');
newdiv.innerHTML = ""+x+"";
newdiv.className = "hover_title_h4";
current_bucket.appendChild(newdiv);
}
check = false;
}
window.onscroll=getH4

javascript Remove value from array, array problems

I am trying to fill an array with strings, the elements that will be added are the HTML of the clicked <\li>, im probably filling it correctly.
My problem is when the user clicks on the checked link again, i want to remove this item from the array
Here is a code sample:
$(document).ready(function(){
var chosen = [];
var chosenCounter = 0;
find("ul").find("li").click(function(){
var checkBox = $(this).find("img").first();
var checkBoxSrc = checkBox.attr("src");
if(checkBoxSrc == "images/unchecked.png"){
checkBoxSrc = "images/checked.png";
checkBox.attr("src",checkBoxSrc);
checkBox = "";
checkBoxSrc = "";
var temp = this.outerHTML;
chosen[chosenCounter] = temp;
chosenCounter ++;
}
if(checkBoxSrc == "images/checked.png"){
checkBoxSrc = "images/unchecked.png";
checkBox.attr("src",checkBoxSrc);
checkBox = "";
checkBoxSrc = "";
for (var j =0; j<=chosen.length; j++){
var tempRemove = this.outerHTML;
chosen.splice( chosen.indexOf( tempRemove ), 1 );
tempRemove = '';
}
}
});
});
I have been trying all functions and ways i found on internet .. but the results doesn't works well, i would be very thankful for a correction and explanation.
Thanks all in advance.
I've gone through an rewritten the code to work much better. There were a number of issues but here is the fixed version that I tested.
Your original code had an if statement and then another if statement. You needed an if and then an else if.
Notice when finding the child element I just use $('img', this) instead of the find operator and first().
Use ID instead of HTML
For debugging there is a console.log statement in there. Remove this so it works in IE.
To add an element to an array use push
No need to loop over splice to remove the item. Just call splice once.
$(document).ready(function () {
var chosenIDs = [];
$("ul li").click(function () {
var checkBox = $('img', this);
var checkBoxSrc = checkBox.attr("src");
var id = $(this).attr('data-id');
console.log(id + ' ' + chosenIDs.length + ' ' + checkBoxSrc);
if (checkBoxSrc == "images/unchecked.png") {
checkBoxSrc = "images/checked.png";
checkBox.attr("src", checkBoxSrc);
chosenIDs.push(id);
}
else if (checkBoxSrc == "images/checked.png") {
checkBoxSrc = "images/unchecked.png";
checkBox.attr("src", checkBoxSrc);
chosenIDs.splice(chosenIDs.indexOf(id), 1);
}
});
});

trying to remove and store and object with detach()

I am trying to remove an object and store it (in case a user wants to retrieve it later). I have tried storing the object in a variable like it says in the thread below:
How to I undo .detach()?
But the detach() does not remove the element from the DOM or store it. I am also not getting any error messages. Here is the code I am using to detach the element:
function MMtoggle(IDnum) {
var rowID = "row" + IDnum;
var jRow = '#' + rowID;
thisMMbtn = $(jRow).find(".addMMbtn");
var light = false;
var that = this;
if (light == false) {
thisMMbtn.bind("click",
function() {
var thisRow = $(this).closest(".txtContentRow");
var thisTxt = thisRow.find(".txtContent");
var cellStr = '<div class = "mmCell prep"></div>';
$(cellStr).appendTo(thisTxt);
$(this).unbind("click");
light = true;
}
);
}
else {
thisMMbtn.bind("click",
function() {
var thisRow = $(this).closest(".txtContentRow");
thisMM = thisRow.find(".mmCell");
SC[rowID].rcbin = thisMM.detach(); //here is where I detach the div and store it in an object
$(this).unbind("click");
light = false;
}
);
}
}
MMtoggle(g.num);
A fiddle of the problem is here: http://jsfiddle.net/pScJc/
(the button that detaches is the '+' button on the right. It is supposed to add a div and then detach it when clicked again.)
Looking at your code I don't think so you need detach for what you are trying to achieve.
Instead try this code.
thisMMbtn.bind("click",
function() {
var thisRow = $(this).closest(".txtContentRow");
var thisTxt = thisRow.find(".txtContent");
var $mmCell = thisTxt.find('.mmCell');
if($mmCell.length == 0){
$mmCell = $('<div class = "mmCell prep"></div>')
.appendTo(thisTxt).hide();
}
$mmCell.toggle();
//$(this).unbind("click");
}
);
Demo

Categories

Resources