1

Think this is a chicken-or-egg or closure problem or something else I'm missing. How do I loop through numerical selector values in JQuery when there is an onClick event handler?

$("#q01").click(function() {
    clearIt();
    $("#item01").show();
}); 
$("#q02").click(function() {
    clearIt();
    $("#item02").show();
});
$("#q03").click(function() {
    clearIt();
    $("#item03").show();
});
user1970570
  • 11
  • 1
  • 2

6 Answers6

3

You might want to try this:

$("[id^=q]").each(function () {
    $(this).click(function () {
        clearIt();
        var id = this.id.replace(/[^\d]/g, ''); 
        $("#item" + id).show();
    });
});
palaѕн
  • 64,836
  • 15
  • 100
  • 121
2

It's hard to say exactly, since your example is probably simplified. But try a more general selector, rather than selecting them by ID (you can use $('[id^="q"]'), but that's not really best practice). You're using the ID element like a class, which isn't really what id is for.

If you give them all a class attribute like class="q" for the buttons and class="item" for the targets, you can do something like this:

$('.q').each(function(index) {
    var targetItem = $('.item').eq(index);
    $(this).click(function() {
        clearIt();
        targetItem.show();
    });
});

But it'll be easier, safer, and better practice to specify the target of each clickable element right there in the markup:

<a href="#" class="p" target-index="01">p01</a>
<a href="#" class="p" target-index="02">p02</a>

<div class="item" item-index="01">item01</div>
<div class="item" item-index="02">item02</div>

Then their order and location on the page won't matter. You can just select them directly:

var num = $(this).attr('target-index');
var targetItem = $('.item[item-index="' + num + '"]');

Finally (and my preferred advice), you can put the ID of the target right into the button itself:

<a href="#" class="p" for="item01">p01</a>
<div id="item01">item01</div>

Then you can select the target quickly, safely, and regardless of its location:

var targetItem = $('#' + $(this).attr('for'));

Edit 1:

My first snippet makes assumptions about the order of your elements, because there are lots of ways for the clicked element to be associated with the target item. Only you know the rules for your markup, so only you know what assumptions will work. For example:

var targetItem = $('#' + $(this).attr('id').replace(/^q/, 'item'));
var targetItem = $(this).siblings('[id^="item"]');
var targetItem = $(this).children('[id^="item"]');
var targetItem = $(this).parents('[id^="item"]');
var targetItem = $(this).find('[id^="item"]');

If you don't use the target-index technique I suggested, you'll have to make at least some assumptions based on your knowledge of the HTML.


Edit 2:

Based on your comment that you're trying to trigger the handlers, rather than improve your event-binding code, you can do that like this:

//quick and dirty zero-padding function from http://stackoverflow.com/q/10073699/399649
var pad = function(num, size) {
  return (Math.pow(10, size) + ~~num).toString().substring(1);
};
for(var i = 1; i < $('[id^="q"]').length; i++) {
    var index = pad(i, 2);
    $('#q' + index).trigger('click');
}
Justin Morgan
  • 27,557
  • 11
  • 71
  • 100
1

You can utilize jquery starts with selector. While using a starts with selector it always advised to use a context within the elements reside. But you can also use a class name for the buttons and bind it with click event with single selector.

$('[id^=q]','çontext').click(function(){...}

or

$('input.classSelector',context).click(function(){...})

With Id Selector

http://jsfiddle.net/Beffv/

function clearIt()
{
$('[id^=item]','#section').hide();
}
clearIt();
$('[id^=q]','#section').click(function(){

    clearIt();
  $('#item' + $(this).attr('id').replace('q',''),'#section').show();
});

With Class Selector

http://jsfiddle.net/vrVcL/

Html

<div id="section">
<input id="q01" type="button" class="itemBtn" value="q1"/>
<input id="q02" type="button" class="itemBtn" value="q2"/>
<input id="q03" type="button" class="itemBtn" value="q3"/>

<div id="item01" class="itemDiv">test</div>
<div id="item02" class="itemDiv">test2</div>
<div id="item03" class="itemDiv">test3</div>
</div>

Script

    function clearIt()
{
$('.itemDiv','#section').hide();
}
clearIt();
$('.itemBtn','#section').click(function(){
    clearIt();
    $('.itemDiv:nth-of-type(' + parseInt($(this).attr('id').replace('q','')) + ')','#section').show();
});
PSL
  • 120,386
  • 19
  • 245
  • 237
  • @Roasted, @Palash - And what if he has an element `` or some other element with an `id` starting with `q`? – Zachary Kniebel Apr 12 '13 at 19:31
  • You need to give a context – PSL Apr 12 '13 at 19:34
  • Specify that in your answer, then – Zachary Kniebel Apr 12 '13 at 19:35
  • 1
    +1 for being the first to suggest the selector and the only one to specify that it must have a context! This is the best answer because of the explanation! Combine it with @PalashMondal's string replace and it is perfect! – Zachary Kniebel Apr 12 '13 at 19:59
  • @ZacharyKniebel Not that it matters, but I think I was first to suggest the class selector. The problem with using a context, unfortunately, is that it should still work if the elements aren't close together (so there's no meaningful wrapping element to use as a context). I'm still of the opinion that matching the ID against a pattern, then pulling an index out of it, is really the wrong way to go. The ID is being used more like a class here, which isn't what it's for. Instead of `id="q01|q02|..."`, it should be `class="q" index="01|02|03|..."`. – Justin Morgan Apr 12 '13 at 20:20
  • 1
    @JustinMorgan if you are using a class selector and you know that you are using these class names uniquely for a purpose, yes there is no need for context. But it is always safe to use a context. Context doesn't mean that you need to wrap them. This is just in the example where it is wrapped. In the original html wrapper could be the actual wrapper used for a section itself. My exaple with class is different it just uses the nth-type-of selector again. if i had such a req i would definitely have gone with a class selector than id. – PSL Apr 12 '13 at 20:25
  • @JustinMorgan - I agree with both you and PSCoder on the `class`/`id` implementation decision; I have made several posts about using classes as flags and identifiers, and I agree that using `id` for this situation was likely not the best way to go. Additionally, if I was mistaken regarding who posted the pattern matching selector first then I apologize (I cannot remove that from my previous comment, as it is too late to edit). On that note, I also agree that pattern matching should be avoided, if possible, but the OP doesn't leave much room for this, but I also agree with the need for context. – Zachary Kniebel Apr 12 '13 at 20:53
  • @ZacharyKniebel i think i was the first one to mention id start with selector, but class selector i am not sure.. Probably you meant about Id seletor that.. – PSL Apr 12 '13 at 20:57
  • @PSCoder - When I made that comment, I had thought that you were the first to suggest the `$('[id^=q]')` selector. I should have clarified that in my original comment. Perhaps I was mistaken, but I still think that you had the best solution and explanation, so it's inconsequential as pertaining to my support of this solution :) – Zachary Kniebel Apr 12 '13 at 21:00
  • Wow thank you @PSCoder and all who weighed in on this one: AtZacharyKniebe, AtJustinMorgan and anyone else - greatly appreciated. In future, I'll provide more context because I can see how it's ambiguous otherwise. – user1970570 Apr 12 '13 at 21:02
  • @ZacharyKniebel and PSCoder - Don't worry about it, I didn't mean to make an issue out of who was first. That shouldn't matter anyway. – Justin Morgan Apr 12 '13 at 21:07
  • @JustinMorgan - No worries - it provided me the opportunity to clarify, anyway, so that was good. Reading that post back, I definitely meant to C&P the selector but forgot to. It actually sounds kind of silly on its own; as though he was the first one to suggest the use of a selector at all! haha :) We all provided good information and feedback, we all helped the poster, and we all helped to make our answers better by providing each other with our opinions. I think this was a pretty big success :) – Zachary Kniebel Apr 12 '13 at 21:13
0

You can use jquery each():

SEE DEMO

$('[id^=q0]').each(function (i) {
    ++i;
    $(this).click(function () {
        $('#item0' + i).show();
    });
});
A. Wolff
  • 72,298
  • 9
  • 84
  • 139
  • what if, in the markup, `
    Click
    ` is present before `
    Click
    `?
    – Ejaz Apr 12 '13 at 19:39
  • @Justin Morgan same thought. – Ejaz Apr 12 '13 at 19:42
  • @Ejay - My code is an example. I can't see the HTML, so I made assumptions about its structure. In fact, there are lots of ways to do this, but finding the right target requires making some kind of assumption about it. See my edit. – Justin Morgan Apr 12 '13 at 20:00
  • right :) I just wanted to point out that relying on `index` isn't robust – Ejaz Apr 12 '13 at 20:12
0

javascript

        var items= $('.item');
        $('.btn').click(function(){
           var num = $(this).attr('id').match(/\d+/ig)
           var item = $('#item'+num[0]+'.item');
           if(item.length){
              clearIt();
              item.show();
           }
        })

        function clearIt(){
           items.hide();
        }

markup

     <a id="q01" href="#" class="btn">Q01</a>
     <a id="q02" href="#" class="btn">Q02</a>
     <a id="q03" href="#" class="btn">Q03</a>

     <div id="item01" class="item">Item 01</div>
     <div id="item02" class="item">Item 02</div>
     <div id="item03" class="item">Item 03</div>

This way you could add and remove the show/hide toggling by simply removing the class .btn or .item.

Ejaz
  • 8,229
  • 3
  • 31
  • 48
0

You should try data- attributes. In this way, you don't need any string replacement. Just keep the target's id in a data attribute, in this example I use data-target. You can change to any name you like. data-show , data-blah.

HTML

<button type="button" class="clear-it" data-target="item01">Clear it and 
show the target</button>

JavaScript

$('.clear-it').each(function() {
  clearIt();
  $(this).click(function() {
    var whereToGo = this.getAttribute('data-target');
    // or var whereToGo = this.dataset.target;
    // or var whereToGo = $(this).data('target');

    $("#" + whereToGo).show();
  });
})
Wit
  • 455
  • 3
  • 16