Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialTomas Pavlik
26,726 PointsPlaying with variables - something's wrong
Hi all, I was trying to change the original code using variables. Everything works fine, well, almost fine, except one thing..
I hope it is understandable, English is not my first language:
If a create a variable called $button and assign new button element to it:
var $button = $("<button>Reveal it!</button>");
and then try to use this variable instead of accesing the button:
$button.click(function() {
$(this).prev().show().click(function() {
etc etc
});
only the second button works fine (it disappears when i click on it and appeals again when I click on the spoiler text). There is no reactin when I click on the first button.
Here is the complete code:
var $spoilerSpan = $(".spoiler span");
var $spoiler = $(".spoiler");
var $button = $("<button>Reveal it!</button>");
$spoilerSpan.hide();
$spoiler.append($button);
$button.click(function() {
$(this).prev().show().click(function() {
$(this).hide();
$(this).next().show();
});
$(this).hide();
});
What's wrong with the firstt button, why it does not react when I'm clcking on it? I appreciate any advice.
Thanks for help.
Tomas Pavlik
26,726 PointsHi Andrews, here´s the complete HTML, JS file is just above.
<!DOCTYPE html>
<html>
<head>
<title>Star Wars Spoilers</title>
<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" title="no title" charset="utf-8">
</head>
<body>
<img src="img/deathstar.png" />
<p class="spoiler">
<!--Spoiler:-->
<span>Darth Vader is Luke Skywalker's Father! Noooooooooooo!</span>
</p>
<p class="spoiler">
<!--Spoiler:-->
<span>Luke Skywalker and Leia are siblings!</span>
</p>
<script src="http://code.jquery.com/jquery-1.11.0.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
4 Answers
Sean T. Unwin
28,690 PointsHi Tomas Pavlik,
The issue is with multiple instances where only the last button appended is getting the click
listener.
This is caused because $button
is only one element. In order to get all instances of $button
we can create a new variable that will have the array of $button
s after they have been added to the DOM.
We can accomplish this by adding a line after $button
is append
ed:
var $buttons = $spoiler.children($button);
/* OR ( each do the same thing )*/
var $buttons = $('.spoiler > button');
I would declare $buttons
with the other variables at the beginning, but don't set it yet.
Using your original code, we would now have:
var $spoilerSpan = $(".spoiler span");
var $spoiler = $(".spoiler");
var $button = $("<button>Reveal it!</button>");
var $buttons;
$spoilerSpan.hide();
$spoiler.append($button);
$buttons = $spoiler.children($button);
// Notice we changed from $button to $buttons
$buttons.click(function() {
$(this).prev().show().click(function() {
$(this).hide();
$(this).next().show();
});
$(this).hide();
});
Sean T. Unwin
28,690 PointsNesting Event Listeners (declaring a listener within another) could be considered bad form, I would think. A concept to consider could be to separate the logic so that component functionality is not intertwined. Even though $spoilerSpan
and $button
are parts of the $spoiler
component, they are distinct entities.
Having this in mind could lead us to create an individual click
listener for each:
$button.click(function() {
$(this).hide();
$(this).prev().show();
});
$button.prev().click(function() {
$(this).hide();
$(this).next().show();
});
You will notice the code is little more spread out now. The benefits, however, are that not only is it easier to read and understand, but the Event Listeners of each object (or entity) are their own. They are not bound to one another this way. Sure, they are related and the Listeners do similar tasks, yet they are different and, more importantly, we have removed any scope confusion with nested use of $(this)
, and also they don't need each other to survive. The $spoiler
component needs them but they don't need each other. This line of thinking is at the core of modular development and Object Oriented Programming.
Another concept that could be touched on here is DRY (Don't Repeat Yourself). You will notice that the click
listeners are fairly similar to each other (which is likely one of the reasons you grouped them together initially). So with DRY in mind, when we repeat lines of code, especially multiple lines, it is time to think about if a function can be created to help with typing the same thing more than once. This can also help reduce typos as well.
So for this function we definately want to use $(this).hide()
and we also want to show()
a sibling. Since the value of $(this)
can be different we need to have our function be able to take two arguments - an object to hide and an object to show. This could be written similar to the following:
function toggleSpoiler($hide, $reveal) {
$hide.hide();
$reveal.show();
}
Then we can refactor the click
Listeners to include the function call:
$button.click(function() {
toggleSpoiler($(this), $(this).prev());
});
$button.prev().click(function() {
toggleSpoiler($(this), $(this).next());
});
So now our tidy, easy to read code could be similar to:
$(function() {
var $spoilerSpan = $('.spoiler span');
var $spoiler = $('.spoiler');
var $button = $('<button>Reveal it!</button');
function toggleSpoiler($hide, $reveal) {
$hide.hide();
$reveal.show();
}
$spoilerSpan.hide();
$spoiler.append($button);
$button.click(function() {
toggleSpoiler($(this), $(this).prev());
});
$button.prev().click(function() {
toggleSpoiler($(this), $(this).next());
});
});
Tomas Pavlik
26,726 PointsHi Sean, thank you for your explanation. Your code works perfectly and I understand the concept you mentioned. In my code, I followed Andrew and it also works fine, except when I tried to create $button. So I still do not know the answer, why the first button does not react when I click on it...
THX
Tomas
Sean T. Unwin
28,690 PointsI just tried your original code again and it worked for me.
Could you specify the buttons when you say 'first' and 'second' so that it's clear?
Perhaps let us know which browser you are using as well.
why the first button does not react when I click on it
Does this means that you are getting a button that is labeled 'Reveal it!' and when you press the button then there is no hide/show or am I misunderstanding?
Andrews Gyamfi
Courses Plus Student 15,658 PointsHi Sean, the 'first' button is the top-most one; which does not respond to the click event (hide button, show spoiler text when clicked) and then the 'second' button respectively - which responds to the click event.
Instead of all the buttons behaving accordingly with the click event, only the last button of the two responds to the click event. The same undesired results is seen when you add more of the same elements to the page - only the last one behaves accordingly.
So his question I presume is, why is it only the last button responding to the click event and not both buttons as expected?
Sean T. Unwin
28,690 PointsGotcha. I wasn't considering multiple $spoiler
s.
Andrews Gyamfi
Courses Plus Student 15,658 PointsYep Sean, it's all good! :)
Cheers!
Andrews Gyamfi
Courses Plus Student 15,658 PointsHi Tomas, there is one subtle error which is causing only the last button to respond to the click event handler.
So you declared:
var $button = $("<button>Reveal it!</button>");
which you used to dynamically create a new button here:
$spoiler.append($button);
However, to apply the click event to the buttons, you need to use a valid and appropriate JQuery element | CSS selector as per the code below:
var $button = $("<button>Reveal it!</button>");
var $spoilerSpan = $(".spoiler span");
var $spoiler = $(".spoiler");
//var $button = $("Reveal it!");
$spoilerSpan.hide();
$spoiler.append($button);
$("button").click(function() {
console.log("I have been clicked");
$(this).prev().show().click(function() {
$(this).hide();
$(this).next().show();
});
$(this).hide();
});
It works correctly!
Cheers!
Sean T. Unwin
28,690 PointsYour example will select all buttons in the DOM.
Once $button
is append
ed it becomes a valid jQuery element and can be accessed via the DOM, provided $button
was defined with valid HTML.
Andrews Gyamfi
Courses Plus Student 15,658 PointsYes, the selector would select all buttons in the DOM which I believe is not much of a problem in this context - it was the same selector the instructor (Andrew) used in the video actually. I believe the degree of specificity of the selector is primarily based on the developer's requirements as well as other factors such as DOM structure, etc. :)
Cheers!
Sean T. Unwin
28,690 PointsAh. I see now.
Cheers. :)
Tomas Pavlik
26,726 PointsHi guys, thank you so much for your help :-) I think I am gettint it. Will need a bit more time and a bit more practice, but your explanations helped a lot. Thanks again for you help and for your patience, I know I am not the best in written english communication :-) Tomas
Andrews Gyamfi
Courses Plus Student 15,658 PointsAndrews Gyamfi
Courses Plus Student 15,658 PointsAre you able to post your HTML code as well so it gives a much wider context please? And your complete JavaScript code please?