I have the following function that fades each line of an unordered-list in and out. Right now it works for #skills_hints but I want to use it for other #named_hints. Is there a way I can reuse this code over multiple unordered lists without copying and pasting the entire code and just changing the IDs?
$(document).ready(function(){
$('#skills_hints .hint');
setInterval(function(){
$('#skills_hints .hint').filter(':visible').fadeOut(800,function(){
if($(this).next('li.hint').size()){
$(this).next().fadeIn(800);
}
else{
$('#skills_hints .hint').eq(0).fadeIn(800);
}
});
},7000);
});
The following will do exactly what you have posted and the doit function (you should rename this obviously) can be called again and again passing a different id.
function doit(id){
$('#'+id+' .hint');
setInterval(function(){
$('#'+id+' .hint').filter(':visible').fadeOut(800,function(){
if($(this).next('li.hint').size()){
$(this).next().fadeIn(800);
}
else{
$('#'+id+' .hint').eq(0).fadeIn(800);
}
});
},7000);
}
$(function(){
doit('skills_hints');
})
A nice trick is turning this into a closure, in case you need a separate function to use as a callback, etc
function doit(id_name){
var id = '#'+id_name + '.hint';
return (function(){
$(id);
setInterval(function(){
$(id).filter(':visible').fadeOut(800,function(){
if($(this).next('li.hint').size()){
$(this).next().fadeIn(800);
}
else{
$(id).eq(0).fadeIn(800);
}
}
},7000);
})
}
//examples
doit_skills = doit('skills_hint');
doit();
doit_foo = doit('foo');
doit_foo()
names = ['a', 'b'];
for(var i=0; i<names.length; i++){
doit(names[i])();
}
You can transform your code into a jQuery Plugin (http://docs.jquery.com/Plugin)
Related
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I'm using a plugin that uses jQuery(document).on() to activate a modal. I have a bunch of modals on the page.
If I manually create an .on for each modal opening/closing everything works as
jQuery(document).on('opened', '[data-remodal-id=modal-1]',
function() {
player1.api('play')
});
jQuery(document).on('closed', '[data-remodal-id=modal-1]',
function(e) {
player1.api('unload')
});
jQuery(document).on('opened', '[data-remodal-id=modal-2]',
function() {
player2.api('play');
});
jQuery(document).on('closed', '[data-remodal-id=modal-2]',
function(e) {
player2.api('unload');
});
However this is a managed page, that could need 2,3 or 10 modals with their own content. I'm trying to do what I did above, only dynamically. Here's my attempt, and I can see why it doesn't work, but I have no idea how to approach this properly.
var countPlusOne;
for (i=0;i<players.length;i++){
countPlusOne=i+1;
var dataSelector = '[data-remodal-id=modal-'+countPlusOne+']';
jQuery(document).on('opened', dataSelector, function () {
players[i].api('play');
});
jQuery(document).on('closed', dataSelector, function (e) {
players[i].api('unload');
});
}
Hopefully it gives you some idea of what i'm trying to do? Is it even possible?
Per my understanding, you have dynamic elements and have to bind events to them.
You can try something like this:
var count = 1;
function addInput(){
var content = document.getElementById("content");
var input = "<input id='txt_"+count+"' class='input'>";
count++;
content.innerHTML+=input;
}
function registerEvents(){
$(document).on("blur", ".input", function(){
console.log($(this).attr("id"));
})
}
registerEvents();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="content"></div>
<button onclick="addInput()">Add input</button>
If playerX are global vars, you could refactorize your code to this:
jQuery(document).on('opened closed', '[data-remodal-id]', function (e) {
window["player" + $(this).data("remodalId").replace('modal-' ,'')].api(e.type === "opened" ? 'play' : 'unload');
});
But i guess you don't need all this different playerX variables anyway.
Ultimately, your api() should handle player id and it would be called like that e.g:
player.api(1, 'play');
EDIT: Ok i'm miss this part in OP
I'm using a plugin
So you shouldn't override api() method.
EDIT2: to answer your question, using closure, you could use:
var countPlusOne;
for (i = 0; i < players.length; i++) {
(function(i) {
countPlusOne = i + 1;
var dataSelector = '[data-remodal-id=modal-' + countPlusOne + ']';
jQuery(document).on('opened', dataSelector, function() {
players[i].api('play');
});
jQuery(document).on('closed', dataSelector, function(e) {
players[i].api('unload');
});
})(i);
}
I need to improve my jquery code where I repeat my function 6 times!!!
is there away to do a loop to shorten the code ?
(function( jQuery ){
jQuery.fn.vesta = function(imgN){
var imgPath = "http://localhost:8080/mhost/media/magentohost/vesta/vesta"
var currImg = imgPath + imgN + ".png";
var targetImg = jQuery('.img-browser img');
jQuery('.img-browser img').attr('src', currImg);
}
})( jQuery );
jQuery('.vesta1').on('click', function (e) {
jQuery('.vesta1').vesta(1);
});
jQuery('.vesta2').on('click', function (e) {
jQuery('.vesta2').vesta(2);
});
jQuery('.vesta3').on('click', function (e) {
jQuery('.vesta3').vesta(3);
});
jQuery('.vesta4').on('click', function (e) {
jQuery('.vesta4').vesta(4);
});
jQuery('.vesta5').on('click', function (e) {
jQuery('.vesta5').vesta(5);
});
jQuery('.vesta6').on('click', function (e) {
jQuery('.vesta6').vesta(6);
});
You can DRY this up by using a common class, and a data attribute to specify the parameter to send to your vesta function:
<div class="vesta" data-vesta="1">1</div>
<div class="vesta" data-vesta="2">2</div>
<div class="vesta" data-vesta="3">2</div>
Then there is no need to loop at all:
$('.vesta').on('click', function (e) {
$(this).vesta($(this).data('vesta'));
});
Use a common class and a data attribute
jQuery('.vesta').on('click', function (e) {
var elem = $(this);
elem.vesta(elem.data("ind"));
});
and the HTML
<div class="vesta vesta1" data-ind="1">
Just put it into a for loop, and take advantage of the dynamic nature of JavaScript:
for (var i = 1; i <= 6; i++) {
$('.vesta' + i).on('click', (function (index) {
return function (e) {
$('.vesta' + index).vesta(index);
};
})(i));
}
I suppose you need the this reference along with some hack kind of thing
$('[class*=vespa]').on('click', function(e){
$(this).vesta(+(this.className.match(/vespa(\d+)/)[1]))
});
Here, we capture elements which have a class that matches at least vespa and then we use some bit of regex to match the digits after vespa and + unary operator changes the String version of numbers into actual numbers.
It would be quite easy if you can alter the structure of the HTML.
You would give all elements the same class, say vesta. But you also give them an attribute, say data-number. For example, like this:
<div class="vesta" data-number="4"></div>
Then, your jQuery code would be as simple as:
$(document).on({
click: function() {
var $this = $(this),
number = +$this.data('number');
$this.vesta(number);
}
}, '.vesta');
Edit:
I was a bit lazy with explaining the code snippet that I have provided an hour ago, but I am modifying my post now in response to the comments.
This code snippet will allow you to apply listeners from '.vesta1' elements to '.vestaN'
[#Variable]
NumberOfClasses - is the positive integer after 'vesta'. Eg: vesta1 ,vesta2, vesta100 ... etc
var NumberOfClasses=6;
for(var i=1;i<=NumberOfClasses;i++){
var className = '.vesta'+(i+1);
jQuery(className ).on('click', function (e) {
$(this).vesta(i);
});
}
<p>Success login. You will be redirected in <span class="counter">10</span> second(s).</p>
<p>Wrong username/password. You will be redirected in <span class="counter">10</span> second(s).</p>
<script type="text/javascript">
function countdown() {
var i = document.getElementByClassName('counter');
if (parseInt(i.innerHTML)<=0) {
location.href = 'login.php';
}
i.innerHTML = parseInt(i.innerHTML)-1;
}
setInterval(function(){ countdown(); },1000);
</script>
Before it was id=counter, and it works for one span tag. But I want two tags to share the same function. I tried changing it to getElementByClassName but it doesn't work. Can anyone tell me why?
There is no function getElementByClassName(), but just getElementsByClassName() (Note the plural for element*s*!). This returns a NodeList, which you then have to traverse:
function countdown() {
var i = document.getElementsByClassName('counter');
for( var j=0; j<i.length; j++ ) {
if (parseInt(i[j].innerHTML)<=0) {
location.href = 'login.php';
}
i[j].innerHTML = parseInt(i[j].innerHTML)-1;
}
}
setInterval( countdown,1000);
PS: In your setInterval() you do not need a function expression - just give it a reference to the function itself (without calling it!).
document.getElementsByClassName('counter') will return an array.
'getElements' - So to target an element in that array would be like :
document.getElementsByClassName('counter')[0]
or loop through them :
var counters = document.getElementsByClassName('counter');
for(var i=0, len=counters.length; i<len; ++i) {
counters[i].innerHTML = 'I am content ' +i;
}
Edit : - note the 'getElement s ByClassName' ( plural elements ) as others have pointed out
There's no getElementByClassName(); in Javascript. You should use getElementsByTagName();
Or, use jQuery selector $('.counter'); (need jQuery library).
For more information, look at [this][1].
I have in Javascript:
for ( i=0; i < parseInt(ids); i++){
var vst = '#'+String(img_arr[i]);
var dst = '#'+String(div_arr[i]);
}
How can I continue in jQuery like:
$(function() {
$(vst).'click': function() {
....
}
}
NO, like this instead
$(function() {
$(vst).click(function() {
....
});
});
There are other ways depending on your version of jquery library
regarding to this, your vst must need to be an object which allow you to click on it, and you assign a class or id to the object in order to trigger the function and runs the for...loop
correct me if I am wrong, cause this is what I get from your question.
$(function() {
$(vst).click(function() {
....
}
})
You can use any string as element selector param for jQuery.
Read the docs for more information.
http://api.jquery.com/click/
http://api.jquery.com/
You can pass a String in a variable to the $() just the way you want to do it.
For example you can do:
var id = 'banner';
var sel = '#'+id;
$(sel).doSomething(); //will select '#banner'
What's wrong is the syntax you are using when binding the click handler. This would usually work like:
$(sel).click(function(){
//here goes what you want to do in the handler
});
See the docs for .click()
Your syntax is wrong, but other than that you will have no problem with that. To specify a click:
$(function() {
for ( i=0; i < parseInt(ids); i++){
var vst = '#'+String(img_arr[i]);
var dst = '#'+String(div_arr[i]);
$(vst).click(function (evt) {
...
});
}
})
Note that since vst is changing in the loop, your event code should also be placed in the loop.
EDIT: Assuming you want the same thing to happen for each image and each div, you could also do something like this:
$(function () {
function imgEventSpec($evt) {
// image clicked.
}
function divEventSpec($evt) {
// div clicked.
}
for (var idx = 0; idx < img_arr.length && idx < div_arr.length; idx ++) {
$("#" + img_arr[idx]).click(imgEventSpec);
$("#" + div_arr[idx]).click(divEventSpec);
}
});
The problem here is that page at alert has the final value of i.. any solution to this?
for(var i=start;i<=end;i++)
{
num=pageNumber.clone();
num.click(function(event)
{
event.preventDefault();
var page=i;
alert(page);
// drawPager();
});
num.find("span").text(i);
if(i==curPage) {
num.find("span").addClass("current");
num=num.find("span");
}
$("#pager>div").append(num);
}
You should do something like this:
num.click(function(i) {
return function(event) {
event.preventDefault();
var page = i;
alert(page);
}
}(i));
This would make an extra enclosure, so i wouldn't get overwritten.
You need to add the handler in a separate function that takes i as a parameter.
For example:
for(var i=start;i<=end;i++) {
handlePage(i);
}
function handlePage(i) {
num=pageNumber.clone();
num.click(function(event)
{
event.preventDefault();
var page=i;
alert(page);
// drawPager();
});
num.find("span").text(i);
if(i==curPage) {
num.find("span").addClass("current");
num=num.find("span");
}
$("#pager>div").append(num);
}
This way, a separate closure (with a separate i parameter) will be generated for each function call.