I have a lot of text in a few sections for one page. I want to use a show / hide to expand and close the extended detailed if the user wants to read more.
At the moment I have a solution, the open link appears in the middle of the text where I want it, but when the user clicks to extend the text, the link stays in the same place… I would like it to appear at the bottom of the extended text.
I am not sure how to change this. Do you have any ideas?
I have a related problem trying to change/update text using slideToggle(). The code below gets me partway there, the page loads with the text, “Show Data Table”. The first click will display the table and change the text, but while subsequent clicks can close the table, the text never changes back.
Am I using .is(‘visible’) wrong?
I know that .is() works on booleans, Where can I learn more about it?
#table gets immediately {display:block;} and then growing height
so $(‘#Table’).is(‘:visible’), and text-line is “Hide Data Table”
Second click:
slideToggle is running backward
#table has still {display:block;} while shrinking height
Just at the click: still $(‘#Table’).is(‘:visible’), and text-line remains “Hide Data Table”
Only if slideToggle is ready, #table gets {display:none;}, but that’s too late to switch the text!
Conclusion: there has to be made a delay of 150ms (or a bit more for safety), before the if statement can go on.
In normal javascript you can do that with a setTimeout() function, I don’t know how that can be accomplished in jQuery, but I guess something like:
$(function(){
$("#Header").click(function(){
$("#Table").slideToggle(150);
setTimeout(toggleText, 200);
});
});
function toggleText(){
if ($('#Table').is(':visible')) {
$("#Header").text('Hide Data Table');
} else {
$("#Header").text('Show Data Table');
}
}
Or to be independent from the visibility, just check the content of the text in the #Header; something like:
$(function(){
$("#Header").click(function(){
$("#Table").slideToggle(150);
if ( $("#Header").text.is( "Show Data Table" ) ) {
$("#Header").text('Hide Data Table');
} else {
$("#Header").text('Show Data Table');
}
});
});
I’ve only gotten to page 35 in my jQuery book, so I’ve got you beat for jQuery-noobdom!
Thanks, the first example you posted worked like a charm. I’d prefer the second as it doesn’t rely on timing (?), but I couldn’t get it to work - the text would never change.
How common are timing issues where you’d need to use setTimeout()?
Timing things can be different: if you have to wait until something has happened, sometimes you can embed it in the function itself (for instance: after a loop has taken place), and sometimes you’ll need the timeOut() function.
Yes I prefer the second too: indeed it doesn’t rely on timing.
I tried to get it working (didn’t test it before), and no success either: lack of jQuery knowledge!
But I have mixed up the second with normal javascript, which I can oversee.
The principle is this:
<p id="showme">Show more please!</p>
<div id="lorem">
<h2>More!</h2>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem nonummy nibh
...
</div>
<script type="text/javascript">
$(document).ready(function(){
$("#showme").click(function(){
$("#lorem").slideToggle(500);
var showme=document.getElementById('showme');
if (showme.innerHTML=="Show more please!"){
showme.innerHTML="Hide more please!";
}
else {
showme.innerHTML="Show more please!";
}
});
});
</script>
For the accessibility I added some extras: if the visitor is browsing without javascript, the More-text may not be invisible forever! So without js the More-text has to be always visible, and the click-option has to be hidden:
#showme {
....
display: none;
}
<script type="text/javascript">
$(document).ready(function(){
var showme=document.getElementById('showme');
showme.style.display="block";
var lorem=document.getElementById('lorem');
lorem.style.display="none";
$("#showme").click(function(){
$("#lorem").slideToggle(500);
if (showme.innerHTML=="Show more please!"){
showme.innerHTML="Hide more please!";
}
else {
showme.innerHTML="Show more please!";
}
});
});
</script>
Thanks for that. This is similiar to the example in my book. However, it doesn’t work when I apply it to my situation, not sure why. I’m showing/hiding a table, and that might have something to do with it…
I like how the latest example is set up for non-JS situations, and I’ve added that to my code.
I think what I’d like to do is explore the use of a call-back to eliminate the setTimeout(). Wouldn’t that be the cleanest way to do this when using the setTimeout() is the ONLY way I’ve gotten this to work so far?
Adding your suggestions, the table’s initial display is ‘table’, and the header’s initial display is ‘none’ in the CSS file.
$(function(){
// if javascript is not enabled, no toggle is possible:
// the Table is visible by default, and "Show ..." is hidden by default.
// if JS is enabled, Header text has to be visible
$('#Header').css('display', 'block');
// if JS is enabled, 'Show..' text has to be invisible before toggling
$('#Table').css('display', 'none');
$("#Header").click(function(){
$("#Table").slideToggle(150, function(){
if ($('#Table').is(':visible')) {
$("#Header").html('Hide Data Table ↑');
} else {
$("#Header").html('Show Data Table ↓');
}
});
});
});
I’m a jquery beginner also but I believe you can accomplish this easily by using a callback in jquery which will wait for the animation to complete before running the check for visible.
myHeader.click(function () {
myTable.slideToggle("slow", function () {
// Animation complete.
if (myTable.is(':visible')) {
myHeader.text('Hide Data Table');
} else {
myHeader.text('Show Data Table');
}
});
});
});
Otherwise the jquery just gets on with the next rule while the animation is running and of course its still visible until the animation is complete.
Full example.
e.g.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Untitled Document</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js "></script>
<style type="text/css">
#Header{cursor:pointer}
</style>
</head>
<body>
<div id="TableContainer">
<h1 id="Header">Hide Data Table</h1>
<table id="Table" width="100%">
<tr>
<td>This is a test</td>
</tr>
<tr>
<td>This is a test</td>
</tr>
<tr>
<td>This is a test</td>
</tr>
<tr>
<td>This is a test</td>
</tr>
<tr>
<td>This is a test</td>
</tr>
<tr>
<td>This is a test</td>
</tr>
</table>
</div>
<script type="text/javascript">
$(function () {
var myHeader = $("#Header");
var myTable = $("#Table");
myHeader.click(function () {
myTable.slideToggle("slow", function () {
// Animation complete.
if (myTable.is(':visible')) {
myHeader.text('Hide Data Table');
} else {
myHeader.text('Show Data Table');
}
});
});
});
</script>
</body>
</html>
Ah ok I see Francky mentioned that a table wrapper was needed also to make it slide nicely. I wonder why jquery doesn’t slide the table nicely by default?
Changing the table to display:inline-block makes it slide nicely but of course ruins the table layout for the data as its no longer a table.
I added a wrapper and it slides nicely now:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Untitled Document</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js "></script>
<style type="text/css">
#Header { cursor:pointer }
td { padding:10px }
</style>
</head>
<body>
<div id="TableContainer">
<h1 id="Header">Hide Data Table</h1>
<!-- add a wrapper for the slideToggle to take effect -->
<div id="Table">
<table width="100%">
<tr>
<td>This is a test</td>
<td>This is a test</td>
<td>Third cell</td>
</tr>
<tr>
<td>This is a test</td>
<td>This is a test</td>
<td>Third cell</td>
</tr>
<tr>
<td>This is a test</td>
<td>This a test</td>
<td>Third cell</td>
</tr>
<tr>
<td>This is a test</td>
<td>This test</td>
<td>Third cell</td>
</tr>
<tr>
<td>This is a test</td>
<td>This is a test</td>
<td>Third cell</td>
</tr>
<tr>
<td>This is a test</td>
<td>This is a test</td>
<td>Third cell</td>
</tr>
</table>
</div>
</div>
<script type="text/javascript">
$(function () {
var myHeader = $("#Header");
var myTable = $("#Table");
myHeader.click(function () {
myTable.slideToggle("slow", function () {
// Animation complete.
if (myTable.is(':visible')) {
myHeader.text('Hide Data Table');
} else {
myHeader.text('Show Data Table');
}
});
});
});
</script>
</body>
</html>
I also notice (from recent experience) that any margins on the element that is sliding causes the slide to be very jumpy so are best avoided.
Also, you could write this:
if (myTable.is(':visible')) {
myHeader.text('Hide Data Table');
} else {
myHeader.text('Show Data Table');
}
as this:
myHeader.text( (myTable.is(':visible')? "Hide" : "Show") + " Data Table");
I thought about doing that but I can never remember that syntax from memory I need to spend more time practising what I’ve learnt as I soon forget.
(the switches for the display=“block”/“none” have now to be written in jQuery language too)
(the trigger line #Header is given a {float: left;} with a clear for the next line, so only the words are clickable and not the whole trigger line)
=======
In my test I didn’t see difficulties.
(shorthand notation if/else) I thought about doing that but I can never remember that syntax from memory.
Yes its not obvious with your example but if you remove the data from the cells you can soon see the difference.
<table id="Table">
<tr>
<td class="topcornerLeft">prisoner<br />
in cell 1</td>
<td class="topcornerRight"><h2>More!</h2></td>
<td class="topcornerRight"><h2>More!</h2></td>
</tr>
<tr>
<td>prisoner<br />
in cell 2</td>
<td class="topcornerRight"><h2>More!</h2></td>
<td><p>Lorem ipsum .</p></td>
</tr>
<tr>
<td>prisoner<br />
in cell 3</td>
<td> hello </td>
<td class="topcornerRight"><h2>More!</h2></td>
</tr>
<tr>
<td class="bottomcornerLeft">dining room</td>
<td class="bottomcornerRight"> test </td>
<td class="topcornerRight"><h2>More!</h2></td>
</tr>
</table>
The cells no longer stretch to fit the table and sit huddled up at the side. Toggle the display and you will see the difference.
The cells no longer have a table parent so an anonymous table parent is created by the browser at the width of the data cells only. That means you won’t get the equal distribution of cells across the width of a 100% table. It just behaves like a width-less table no matter how much you try to change it.
In your example the content in the cells themselves pushes the cells wide until they span al the way across but if you had content that didn’t reach the edge of the cell (as in most table data structures) you would see the table collapse.
Rather than wrapping a div around the table you could in your example add a tbody element inside the table and give that a display:table and 100% width.
tbody{display:table;width:100%}
However if you have a thead section then you can’t use that approach because that would give you two separate tables and no cell alignment between thead and tbody.
There is also an issue in adding display:inline-block to the table as it stops height being a minimum and if you apply a height of 200px to the table the table content overflows and spills out unlike a real table where the table always accommodates the content.
The outcome is that display:inline-block for the table element could work in some limited situations but as an overall solution is totally unreliable. The best advice is to wrap the table in a div instead.
Yes, of course, it’s loud & clear; if you strip of the basic feature of a table, he’ll not behave as a table!
Conclusion: it’s allowed to put the wrapper-div around the table without saddling the html with superfluous code.
Then my 3rd arrow, with the wrapper comeback, should be the definitive [U]180[/U] for one roll out table: