In my case, I have this in each(), I need to use that this from the begining, I know it's blur but you'll understand with the example
jQuery(document).ready(function () {
jQuery('.ids-attributes-wrapper').each(function(){
var size_li = jQuery(this).find('dd.ids-attr-content li.ids-sub-filters').length;
x=3;
jQuery(this).find('dd.ids-attr-content li.ids-sub-filters:gt('+x+')').hide().end().append(jQuery('<li class="ids-sub-filters more">...</li>'));
jQuery(this).find('dd.ids-attr-content li:lt('+x+')').show();
jQuery(this).find('#show-more').click(function () {
x= (x+50 <= size_li) ? x+50 : size_li;
jQuery(this).find('dd.ids-attr-content li:lt('+x+')').show();
//!!! HERE the above this 'jQuery(this)' doesn't refer to the .ids-attributes-wrapper each selector
jQuery(this).find('dd.ids-attr-content li.ids-sub-filters.more').remove();
});
jQuery(this).find('#show-less').click(function () {
if (!jQuery(this).find('dd.ids-attr-content li.ids-sub-filters').hasClass('more')) {
x = 5;
jQuery(this).find('dd.ids-attr-content li').not(':lt('+x+')').hide();
jQuery(this).find('dd.ids-attr-content li.ids-sub-filters:gt('+x+')').hide().end().append(jQuery('<li class="ids-sub-filters more">...</li>'));
}
});
});
});
this context problems in JavaScript are pretty common. I believe that what you are looking for is the following:
jQuery(document).ready(function () {
jQuery('.ids-attributes-wrapper').each(function(){
var size_li = jQuery(this).find('dd.ids-attr-content li.ids-sub-filters').length;
x=3;
jQuery(this).find('dd.ids-attr-content li.ids-sub-filters:gt('+x+')').hide().end().append(jQuery('<li class="ids-sub-filters more">...</li>'));
jQuery(this).find('dd.ids-attr-content li:lt('+x+')').show();
var self=this
jQuery(this).find('#show-more').click(function () {
x= (x+50 <= size_li) ? x+50 : size_li;
jQuery(self).find('dd.ids-attr-content li:lt('+x+')').show();
//!!! HERE the above this 'jQuery(this)' doesn't refer to the .ids-attributes-wrapper each selector
jQuery(this).find('dd.ids-attr-content li.ids-sub-filters.more').remove();
});
jQuery(this).find('#show-less').click(function () {
if (!jQuery(this).find('dd.ids-attr-content li.ids-sub-filters').hasClass('more')) {
x = 5;
jQuery(this).find('dd.ids-attr-content li').not(':lt('+x+')').hide();
jQuery(this).find('dd.ids-attr-content li.ids-sub-filters:gt('+x+')').hide().end().append(jQuery('<li class="ids-sub-filters more">...</li>'));
}
});
});
});
In this example, the context from the previous this is referenced by the new variable self. Also, JavaScript has a built in language feature to handle this. It's the bind function, but it's not used very often and the self method I have demonstrated here is more idiomatic.
Related
I have these 3 jQuery functions that basically do the same thing, but they are a tiny bit different (width of window, and class that is removed/toggled)
I need the functionality of all these 3 functions, but want to somehow combine them/shorten them into one function. I've tried to but my code keeps breaking
Can anyone help to shorten them?
Here are the 3 functions
jQuery(document).ready(function($) {
$('.exampleimg').click(function() {
$('.about').hide(600);
if (($(window).width() > 670) && ($(this).hasClass('exampleimgopen'))) {
$(this).removeClass('exampleimgopen');
} else if ($(window).width() > 670) {
$('.exampleimg').removeClass('exampleimgopen');
$(this).addClass('exampleimgopen');
}
});
});
jQuery(document).ready(function($) {
$('.exampleimg').click(function() {
$('.about').hide(600);
if (($(window).width() < 670) && ($(this).hasClass('exampleimgopen2'))) {
$(this).removeClass('exampleimgopen2');
} else if ($(window).width() < 670) {
$('.exampleimg').removeClass('exampleimgopen2');
$(this).addClass('exampleimgopen2');
}
});
});
jQuery(document).ready(function($) {
$('.exampleimg').click(function() {
$('.about').hide(600);
if (($(window).width() < 540) && ($(this).hasClass('exampleimgopen3'))) {
$(this).removeClass('exampleimgopen3');
} else if ($(window).width() < 540) {
$('.exampleimg').removeClass('exampleimgopen3');
$(this).addClass('exampleimgopen3');
}
});
});
I need the functionality of all these 3 functions, but want to somehow combine them/shorten them into one function.
Generally, a good approach when refactoring similar functions is to create a single factory function that will take your variable data as arguments and return a function that has access to that data via its closure.
function myFactory(conditionFunc, windowWidth, cssClass) {
return function() {
$('.about').hide(600);
var windowCondition = conditionFunc($(window).width(), windowWidth);
if (windowCondition && ($(this).hasClass(cssClass))) {
$(this).removeClass(cssClass);
} else if (windowCondition) {
$('.exampleimg').removeClass(cssClass);
$(this).addClass(cssClass);
}
}
}
Then you can call this function 3 times to build your functions:
// helper methods that will perform '<' or '>' on window width
var lessThan = function(a, b) { return a < b; };
var greaterThan = function(a, b) { return a > b; };
var func1 = myFactory(greaterThan, 670, 'exampleimgopen');
var func2 = myFactory(lessThan, 670, 'exampleimgopen2');
var func3 = myFactory(lessThan, 540, 'exampleimgopen3');
Which you can then pass each into their corresponding listeners.
$('.exampleimg').click(func1);
$('.exampleimg').click(func2);
$('.exampleimg').click(func3);
The advantage of doing things this way is that you only write a single function which then creates different versions of your listener callback functions, based on the data you give to it.
I think is maybe closer to what you wanted, although it's unclear what you wanted to have happen when the width was < 540. Might want to use if .. then ... else instead
jQuery(document).ready(function($) {
$('.exampleimg').click(function() {
var width = $(window).width();
var classes = [];
if (width < 540) {
classes.push('exampleimgopen3');
}
if ($(window).width() < 670) {
classes.push('exampleimgopen2');
}
if ($(window).width() >= 670) {
classes.push('exampleimgopen');
}
classes.forEach(function(class) {
$('.exampleimg').removeClass(class);
if (!$(this).hasClass(class)) {
$(this).addClass(class);
}
});
});
});
Can you help me with the following?
I have this working javascript code.
This code makes the bootstrap columns in a row all an equal height for screens with a bigger width than 640px.
if($(window).width() >= 640){
$(document).ready(function () {
$(".row").each(function () {
var highestBox = 0;
$(".col", this).each(function () {
if ($(this).height() > highestBox) highestBox = $(this).height();
});
$(".col", this).height(highestBox);
});
});
}
Problem
The problem is that if I resize the window from less than 640px to more than 640px, I have to refresh the page in order to see the changes. I'd like a script that automatically checks the window size. I think the .resize() function can help me with this but I can't figure out how to implement it.
You could implement it as follows. Put your code inside a function, let's call it resizeMe() .Then:
var rtime,
timeout = false,
waitingTime = 200,
resizeFinished = function () {
if (new Date() - rtime < waitingTime) {
window.setTimeout(resizeFinished, waitingTime);
} else {
timeout = false;
if ($(window).width() >= 640) {
resizeMe();
}
}
};
$(window).resize(function () {
rtime = new Date();
if (timeout === false) {
timeout = true;
window.setTimeout(resizeFinished, waitingTime);
}
}).resize();
With this function, your function resizeMe() is fired only when the resizing process has finished. That way it is assured that resizeMe()isn't fired at every mouse move but only if waitingTime is reached (200ms). This is quite important for performance. (Thanks to urban pointing at)
You could use following snippet, using window.matchMedia() (support & polyfill) method and binding handler in load/resize window events:
$(window).on('load resize', function() {
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(function() {
if (window.matchMedia("(min-width: 640px)").matches) {
$(".row").each(function() {
var highestBox = Math.max.apply(null, $(this).find('.box').map(function() {
return this.clientHeight; // or $(this).height()
}));
$(this).find('.box').height(highestBox);
});
}
}, 50);
});
EDIT Adding debouncing regarding good point done in #Daenu's answer
just make a function and pass it to window
function resizeMe(){
if($(window).width() >= 640){
$(document).ready(function () {
$(".row").each(function () {
var highestBox = 0;
$(".col", this).each(function () {
if ($(this).height() > highestBox) highestBox = $(this).height();
});
$(".col", this).height(highestBox);
});
});
}
}
then attach an event to window and pass your function
window.addEventListener("resize", resizeMe);
you need to put the
$(document).ready(function () on the top before the if($(window).width() >= 640)
you can use
$( window ).resize(function() function
Based on #Daenu's answer. A compressed/simplified version that is reusing the same timeout:
$(function(){
timeout = null
waitingTime = 200
$(window).resize(function(){
// Clear old timeout
if (timeout)
clearTimeout(timeout)
// Set new
timeout = setTimeout(resizeMe, waitingTime);
})
})
function resizeMe(){
$(".row").each(function () {
var highestBox = 0;
$(".col", this).each(function () {
if ($(this).height() > highestBox) highestBox = $(this).height();
});
$(".col", this).height(highestBox);
});
}
The idea is that if resize is fired while there is an active timeout, we cancel the old and re-schedule a new one
Fiddle: https://jsfiddle.net/hq1jd47v/2/
This question has been asked long ago and answered as well here jQuery load first 3 elements, click “load more” to display next 5 elements, but that was for only one ul element.
However, I want to know how to do the same thing for multiple elements say I have this instead:
<ul id="myList"></ul>
<ul id="myList1"></ul>
<ul id="myList2"></ul>
how do I make the javascript for multiple elements in this case??
$(document).ready(function () {
size_li = $("#myList li").size();
x=3;
$('#myList li:lt('+x+')').show();
$('#loadMore').click(function () {
x= (x+5 <= size_li) ? x+5 : size_li;
$('#myList li:lt('+x+')').show();
$('#showLess').show();
if(x == size_li){
$('#loadMore').hide();
}
});
$('#showLess').click(function () {
x=(x-5<0) ? 3 : x-5;
$('#myList li').not(':lt('+x+')').hide();
$('#loadMore').show();
$('#showLess').show();
if(x == 3){
$('#showLess').hide();
}
});
});
any idea how to do that??
Thanks
Update#1:
This is my other code part for showmore and showless
<div id="loadMore">Load more</div><div id="showLess">show Less</div>
Update#2
what if classes used instead of ids, would that make it easier?? like this:
<ul class="myList"></ul>
<ul class="myList"></ul>
<ul class="myList"></ul>
and each showmore/Less can control one of them. so one to one... is that possible???
You can change your code by using classes instead of ids and a wrapping div; than change accordingly the logic by using element nesting.
Instead of use a variable you can use a HTML attribute to store the current number of showed li for each ul.
Code:
$(document).ready(function () {
$(".wrapper").each(function (index) {
$(this).find('.myList li:lt(' + $(this).attr('viewChild') + ')').show();
});
$('.loadMore').click(function () {
var $myWrapper= $(this).closest('.wrapper');
var x= parseInt($myWrapper.attr('viewChild'),10);
var liSize=$myWrapper.find('.myList li').size();
x = (x + 5 <= liSize) ? x + 5 : liSize;
$myWrapper.attr('viewChild',x)
$myWrapper.find('.myList li:lt(' + x + ')').show();
$myWrapper.find('.showLess').show();
if (x == liSize) {
$myWrapper.find('.loadMore').hide();
}
});
$('.showLess').click(function () {
var $myWrapper= $(this).closest('.wrapper');
var x= $myWrapper.attr('viewChild')
x = (x - 5 < 0) ? 3 : x - 5;
$myWrapper.attr('viewChild',x)
$myWrapper.find('.myList li').not(':lt(' + x + ')').hide();
$myWrapper.find('.loadMore').show();
$myWrapper.find('.showLess').show();
if (x == 3) {
$myWrapper.find('.showLess').hide();
}
});
});
Demo: http://jsfiddle.net/9gxBT/
I'm not sure why the interval isn't looping. I've followed tutorials exactly but not luck. Suggestions?
$(document).ready(function(){
setInterval(function() {
$('.current').removeClass('current').next().addClass('current');
}, 2000);
});
Updated: http://jsfiddle.net/pa7aU/3/
One possible ugly solution:
setInterval(function() {
var $current = $(".current").removeClass("current"),
$next = $current.next();
if ($next.length)
$next.addClass("current");
else
$current.siblings(":first").addClass("current");
}, 2000);
DEMO: http://jsfiddle.net/pa7aU/4/
$(document).ready(function () {
var $li = $('ul li'), i = 0, l = $li.length;
setInterval(function() {
$li.removeClass('current').eq(i % l).addClass('current');
i++;
}, 2000);
});
http://jsfiddle.net/chg4J/
When you reach the last element, you need to go back to the beginning, not use .next().
$(document).ready(function () {
setInterval(function () {
var next = $('.current').removeClass('current').next();
if (next.length == 0) {
next = $("li:first");
}
next.addClass('current');
}, 2000);
});
FIDDLE
When you are at the last li, next() doesnt go to the first.
An easy fix is to add that after :
if($('.current').length <= 0) $('li:first').addClass('current');
Fiddle : http://jsfiddle.net/pa7aU/5/
Just to add something new to the mix:
$(document).ready(function () {
setInterval(function () {
$('li').eq(($('.current').removeClass('current').index() + 1) % $('li').size()).addClass('current');
}, 200);
});
In my game I want to make sure the start button is only clicked once each time it appears to stop it loading the function more than once.
I have written some code that counts the clicks and unbinds if the count is greater than zero.
var oneClick = 0;
$(".start-btn-wrapper").bind("click", function () {
oneClick++;
if (oneClick > 0) {
$(this).unbind("click");
}
newGame();
});
This works fine but I need a way to bind it again ready for the next time it appears (When the restart-btn is clicked) so I have written this to reset the count.
$(".restart-btn").click(function () {
resetGame();
oneClick = 0;
});
For some reason this doesn't do the job and I am looking to SO for a soloution
You're not binding again but just changing oneClick, which isn't even tested as the event handler to $(".start-btn-wrapper") isn't binded anymore.
But instead of unbinding/rebinding, which is costly, why not just test oneClick :
$(".start-btn-wrapper").bind("click", function () {
if (oneClick++ > 0) return;
newGame();
}
Note that if you just need two states, a boolean would be clearer :
var clickable = true;
$(".start-btn-wrapper").bind("click", function () {
if (!clickable) return;
newGame();
clickable = false;
}
$(".restart-btn").click(function () {
resetGame();
clickable = true;
});
jQuery has a built in method for exactly this use. It is .one()
So all you have to do is
$(".start-btn-wrapper").one('click',newGame);
and for the restart
$(".restart-btn").click(function () {
resetGame();
$(".start-btn-wrapper").one('click',newGame);
});
simple
var oneClick = 0;
$(".start-btn-wrapper").bind("click", function () {
if (oneClick > 0) {
return false;
}
oneClick++;
newGame();
});
$(".restart-btn").click(function () {
resetGame();
oneClick = 0;
});
var oneClick = 0;
function clickHandler() {
oneClick++;
if (oneClick > 0) {
$(".start-btn-wrapper").unbind("click");
}
newGame();
}
$(".start-btn-wrapper").bind("click", clickHandler);
$(".restart-btn").click(function () {
resetGame();
oneClick = 0;
$(".start-btn-wrapper").bind("click", clickHandler);
});