views:

290

answers:

9

The size of my JavaScript file is getting out of hand because I have hundreds of links, and each one has its own jQuery function even though they all peform basically the same task.

Here's a short excerpt:

$("#link1").click(function ()
{
  $(".myDiv").hide();
  $("#myDiv1").toggle();
});

$("#link2").click(function ()
{
  $(".myDiv").hide();
  $("#myDiv2").toggle();
});

$("#link3").click(function ()
{
  $(".myDiv").hide();
  $("#myDiv3").toggle();
});

Would there be a way to abstract some of this logic so that I have only a single function instead of hundreds that do the same thing?

+1  A: 

You can just have each click call an external function and pass in a parameter for the number string.

Ex:

$("#link1").click(toggle("1"));
$("#link2").click(toggle("2"));

function toggle(number) {
    $(".myDiv").hide();
    $("#myDiv"+number).toggle();
}
Craig Martek
I like this method the most because you can add any element to any other element. ie. `$("coolLink").click("1");`
MitMaro
As written, your code will call `toggle()` when the `.click()` method is applied, not when the user actually clicks on the link.
Ben Blank
A: 

I think this is a simple refactoring

you could define a function as such

function doSomethingTo(thisDiv)
{
   $(".myDiv").hide();
   $(thisDiv).toggle();
}

and then just reuse it where you need it

$("#link1).click(doSomethingTo(thisDiv));
$("#link2).click(doSomethingTo(thisDiv));
jgarcia
I think you mean "`$("#link1).click(doSomethingTo)`".
Ben Blank
You're kind of right $("#link1").click(doSomethingto("#myDiv1"));
jgarcia
That's still going to call `doSomethingTo()` **immediately**, not assign it as a click handler. I believe you're thinking of `$("#link1").click(function() { doSomethingTo(this); })`.
Ben Blank
+12  A: 

You can add a class to all the links that do the same thing and act with jQuery on that class.

<a href='whatever' id='link_1' class='toggler'>text</a>
<a href='whatever' id='link_2' class='toggler'>text</a>

jQuery code will be:

$(".toggler").click( function(){
    // toggle the divs
    var number = $(this).attr("id").split('_')[1];
    $(".myDiv").hide();
    $("#myDiv"+ number).toggle();
});
Elzo Valugi
IMHO, this is the best approach because when you add more 'links' and 'divs' in your HTML, the code automatically takes care of handling the toggle behavior for you. BTW, Elzo, you need to specify where the 'number' variable come from. Something likevar id = $(this).attr('id');var number = id.substr(id.indexOf('_'));
SolutionYogi
I prefer using traversal mechanisms rather than depending on id formats/values. If the items are related they are usually closely aligned in the DOM as well and can easily be found using next/prev/find/closest etc.
tvanfosson
That's a very good suggestion tvanfossaon. But sometimes it's not possible to keep those elements together in DOM based on your page layout. I also like Soldarnal's idea of using the href attribute for carrying this information. http://stackoverflow.com/questions/1100026/how-can-i-reduce-the-redundancies-in-my-jquery-code/1100078#1100078
SolutionYogi
Curious why you chose the id to hide the associative data? http://stackoverflow.com/questions/364412/associating-data-to-a-dom-element-for-jquery
Soldarnal
Not too often I see a post I'd upvote more than once. Excellent answer!
Neil N
@Soldarnal I know about metadata plugin, but in my opinion its use is for purists. Is nothing wrong with using a class to create an association for common elements. It can be used in css as well in jQuery.@Neil N :) thx | code is poetry
Elzo Valugi
A: 

Building on Craig's solution:

$("#link1, #link2").click(toggle(this));

function toggle(obj) {
    $(".myDiv").hide();
    $("#myDiv" + $(obj).attr("id").replace('link','')).toggle();
}
Nick Sergeant
they have hundred links .. so i won't looks good to write #link1 .. #link100 one by one :) (also it will add more codes)
nightingale2k1
Whoops - didn't read the *whole* thing. Typical me. In that case, Elzo's solution above is best.
Nick Sergeant
A: 

I change the link become like this (i rename the id to just a number)

<a href='#test1' id='1' class='link'> ... </a>
<a href='#test2' id='2' class='link'> ... </a>
<a href='#test3' id='3' class='link'> ... </a>

and then on js:

$(document).ready(function(){
    $('.link').click(function(){
      $('.myDiv').hide();
      var id = $(this).attr('id'); // take the id
      $('#myDiv'+id).toggle(); 
    });
});
nightingale2k1
+3  A: 

The general approach that I use is to use the traversal methods to find related elements rather than using absolute selectors. This will allow you to apply the same code to elements that are similarly configured without any complicated dependencies on the format of the ids, etc. Done correctly it's also reasonably robust against minor changes to the mark up.

For example, say I have a series of links, each followed by a div that will be toggled by clicking on that link. The links each have a particular class so they can easily be referenced.

<a href="#" class="linkClass">Toggle</a>
<div>
     Some content...
</div>

<a href="#" class="linkClass">Toggle</a>
<div>
     Other content
</div>

I would then find all the links by class, then use the next method to find the associated div and toggle it's visibility. Note that this is a simple example. You may need to use more complicated traversal mechanisms and filter by element type or class, too, depending on your exact mark up.

$('.linkClass').click( function() {
    $(this).next().toggle();
});
tvanfosson
If your page layout allows this HTML, I would prefer this approach. :)
SolutionYogi
My understanding of your example is that this makes a lot of assumptions about the relationship of elements on the page. I'm working on a highly stylized page in which the 'next' element is unrelated to the link. (At least I think so).
ipso facto
@ipso facto -- as I said, this example is relatively simple. If it's true that there isn't a discernable DOM relationship between the elements, then you'll need to find some other mechanism. The typical case is that they are related and you can find a mechanism using the traversal to get to the related element. Find/nextall+each, etc.
tvanfosson
+1  A: 
function makeToggler(number) {
    $('#link' + number).click(function() {
        $('.myDiv').hide();
        $('#myDiv' + number).toggle();
    });
}

makeToggler(1);
makeToggler(2);
makeToggler(3);

You can adapt this to meet your naming standards.

Depending on the structure of your divs and links, there are better ways to do it. If you post the structure of your elements, I'll show you one.

SLaks
I think this is the cleanest approach so far, also preferable since the makeToggler() function can be unit tested fairly directly
Frank Schwieterman
+3  A: 

What about adding the ID of your target into the href of the link?

<a id="link1" href="#myDiv1" class="toggle">Toggle 1</a><br/>
<a id="link2" href="#myDiv2" class="toggle">Toggle 2</a><br/>
<a id="link3" href="#myDiv3" class="toggle">Toggle 3</a><br/>

Then you could write a single function like so:

$(".toggle").click(function(e) {
    e.preventDefault();
    $(".myDiv").hide();
    $($(this).attr('href')).toggle();
});


Or another approach I've used:

$(".toggle").each(function(i) {
    $(this).click(function(e) {
     e.preventDefault();
     $(".myDiv").hide();
     $(".myDiv:eq("+i+")").toggle();
    });
});

This one is in the same vein as tvanfosson's idea, using some sort of DOM relationship to link the elements, in this case by assuming that the link elements and the div elements are in the same order on the page.

Soldarnal
That's a good idea.
ipso facto
A: 

throw your makeToggle into a loop?

function makeToggler(number) {
    $('#link' + number).click(function() {
        $('.myDiv').hide();
        $('#myDiv' + number).toggle();
    });
}

for(i=1;i>=#;i++) {makeToggler(i);}

then you could even have it count your links for you, something link this?:

function countElementsByClass(className){
  var count = 0;
  var o = document.getElementsByTagName("a").className;
  for(var i=0;i<o.length;i+){
      if(o[i].className == "accordion/whatever")
          count ++;
      }

  return count;
}

credit: building on SLaCKS solution

Nona Urbiz
Or, `$('a.accordian').length`
SLaks