1

I am using a JQuery cookie library to hide/show elements and then remember the status when the page is reloaded. The working code looks for elements with id #num0 and toggles the next element, and it looks like this:

if(Cookies('divShown0') == 'true') {  //defaults to hidden
    $('#num0').next().show(); // Show if cookie there and 'true'
}
$('#num0').click(function() {
  $(this).next().toggle();
  if(Cookies('divShown0') == 'true') {
    Cookies.set('divShown0', 'false'); // Remember it was hidden
  }
  else {
    Cookies.set('divShown0', 'true');  // Remember it was shown
  }
});

I have multiple of these, each identified by a different #num and stored as a different divShown number. So far I have just added a new code block with new numbers, but obviously this takes up a lot of space. I put the first if() statement into a for() loop, no problem. The second part I broke into a function, toggleShown(num), so I can call $('#num0').click(toggleShown(0));, but this is not working. I suspect that something about the $(this) element isn't properly identifying the next element, but I really don't know.

function toggleShown(num)
  {
   $(this).next().toggle();
   if(Cookies('divShown' + num) == 'true') {
     Cookies.set('divShown' + num, 'false'); // Remember it was hidden
   }
   else {
     Cookies.set('divShown' + num, 'true');
   }
  }

I don't really do Javascript or JQuery, mostly RoR but I am trying to hack this together. Any thoughts on what I am screwing up?

1
  • Right, $(this) will only exist within the originalting function in the scope you are looking for it. The good news is you can either pass it in as a paramater to the toggleShown function, or find it in the DOM within the function by building off of the num param. Commented Sep 16, 2015 at 17:07

5 Answers 5

1

You hunch is correct! $(this) inside of your function does not hold the reference to the original element, because you are in a new function with a new scope.

All you should have to do is change:

$(this).next().toggle();

to

$("#num" + num).next().toggle();

Sign up to request clarification or add additional context in comments.

1 Comment

Hm so I tried changing that and it still isn't working, however now it appears to run the function once for each div on pageload, but not on clicks.
1

You need to add a handler for each DOM element anyway, so you can use something like this:

function addHandler(num) {
    $('#num' + num).click(function() {
        $(this).next().toggle();
        var cookieName = 'divShown' + num;
        if (Cookies(cookieName) == 'true') {
            Cookies.set(cookieName, 'false'); // Remember it was hidden
        }
        else {
            Cookies.set(cookieName, 'true');  // Remember it was shown
        }
    });
}

for (var num = 0; num < numDivs; ++num) {
    addHandler(num);
}

4 Comments

"You need to add a handler for each DOM element anyway..." Not really, they could do $("[id^=num]").click(toggleShown); just once.
While you are technically correct @T.J.Crowder that just opens the door for all sorts of errors later (having other elements id starting with 'num') Why put that constraint on yourself, not naming anything else starting with num?
@A.O.: Trivial to weed them out with filter. I'd rather not create unnecessary long-lived functions.
Ah yes, especially as num grows higher and higher. Then you might want to consider adding a class to the all the #num* divs in order to grab them all. Much faster than ^=
0

You can set the this argument of a function by calling the function through Function.prototype.call(). The first parameter passed will be assigned as this inside the function.

Comments

0

If you are going to have multiple similar divs, instead of just IDs, you could assign a class to all of them and let jQuery add click listeners to all of them at once.

You can then either read the div ID from the ID itself, or you could give each a data attribute.

HTML:

<button class="toggle" data-id="1">First</button>
<button class="toggle" data-id="2">Second</button>
<button class="toggle" data-id="3">Third</button>

<div id="div1">First</div>
<div id="div2">Second</div>
<div id="div3">Third</div>

Javascript:

$('.toggle').click(function(){
    var id = $(this).data('id');
    hideOrShow(id);
})

function hideOrShow(id){
    $('#div'+id).toggle();
}

Comments

0

The second part I broke into a function, toggleShown(num), so I can call $('#num0').click(toggleShown(0));, but this is not working.

You can do

$("#num0").click(toggleShown);

...for just one of them, or

$("[id^=num]").click(toggleShown);

...for all of them at once. (^= means "starts with", so that would be "all elements with an id attribute starting with num"). If you need to weed out ones with other ids (like number):

$("[id^=num]").filter(function() {
    return this.id.match(/^num\d+$/);
}).click(toggleShown);

Either way, you then keep using this within toggleShown, and get the index number from the end of this.id:

function toggleShown() {
    var divName = "divShown" + /\d+$/.exec(this.id)[0];
    $(this).next().toggle();
    if (Cookies(divName) == 'true') {
        Cookies.set(divName, 'false'); // Remember it was hidden
    } else {
        Cookies.set(divName, 'true');
    }
}

Live Example:

$("[id^=num]").click(toggleShown);
function toggleShown() {
  var divName = "divShown" + /\d+$/.exec(this.id)[0];
  $("#" + divName).toggleClass("on");
}
.on {
  color: blue;
}
<div>Click any of the "num"s, and its corresponding "divShown" toggles between blue and normal text</div>
<div id="num0">num0</div>
<div id="num1">num1</div>
<div id="num2">num2</div>
<div id="num3">num3</div>
<hr>
<div id="divShown0">divShown0</div>
<div id="divShown1">divShown1</div>
<div id="divShown2">divShown2</div>
<div id="divShown3">divShown3</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>


Alternately, you can use Function#bind or jQuery.proxy to bind the index to toggleShown, and then use e.currentTarget for this:

$("#num0").click(toggleShown.bind(null, "0"));

then:

function toggleShown(num, e) {
    $(e.currentTarget).next().toggle();
    if (Cookies('divShown' + num) == 'true') {
        Cookies.set('divShown' + num, 'false'); // Remember it was hidden
    } else {
        Cookies.set('divShown' + num, 'true');
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.