SitePoint Sponsor

User Tag List

Results 1 to 15 of 15
  1. #1
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    257
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Change text depending on condition

    Hi,

    I have the following code that I use to loop though records in my database and display the text. For each record I look through the text for a certain piece of text and then split the content it is found. So for example if the database contained a field with the data "this is some text [splithere] this is some more text" then the first part (this is some text) would be displayed and then a more button would be shown. If the user clicked the more button then the rest of the content (this is some more text) would also be shown.

    At the moment the more button is always shown, even if the [splithere] text was not found. What I'd like to do is only show the more button where necessary. I'd also like to change the text from 'more' to 'hide' when the button has been clicked and the additional content is being shown as when they click the link again the text is in fact hidden and so saying 'more' doesnt make any sense.

    Here is the code I use:

    Code:
    <?php
    				$thetask = '1';
    				$number = '1';
    				$sql = "SELECT * FROM cases WHERE task_id = '$thetask' AND company_id = '1'";
    				$sqlresult = mysql_query($sql);
    				while($row = mysql_fetch_array($sqlresult)) {
    				$data = $row["body"];
    				
    
    	?><table width="600" border="0" cellpadding="5" cellspacing="0" bgcolor="#FFFFFF">
      <tr>
        <td><?php
     
    $parts=explode('[splithere]',$data);
     
    foreach($parts as $i=>$part){
      if($i==0){
        echo '<p>'.$part.'</p>';
         ?><span class="style2"> <?php
      }else{
        echo '<p class="hidden" name="p'.$number.'">'.$part.'</p>';
      }
    }
     
    echo '<script type="text/javascript">
    function more(display, id){
      var e=document.getElementsByTagName("p");
      for(i=0; i<e.length; i++){
       if(e[i].className=="hidden"&&(e[i].getAttribute("name")==id || id=="")) e[i].style.display = display==e[i].style.display?"none":display;
      }
    }
     
    more("none","");
    </script>';echo '<p><a href="javascript:void(0)" onclick="more(\'block\',\'p'.$number.'\')">More</a></p>';
    $number++;
    ?></span>
          
    </td>
      </tr>
    </table>
    <?php } ?>
    Does anyone have the first idea how I can do this?

    Thanks a lot

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,684
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    From the look of your code I see that you plan to only have one split throughout the text. This is useful because the more/hide stuff can be performed without needing separate individual identifiers on each of the elements.

    I'll start by using an html representation of how the existing php code shows three different lines, one with more text, another one without, and the last with more as well.

    Code html4strict:
    <table width="600" border="0" cellpadding="5" cellspacing="0" bgcolor="#FFFFFF">
        <tr>
    		<td>
    			<p>This is the first </p>
    			<span class="style2">
    			<p class="hidden" name="p1"> that will be shown.</p>
    			<script type="text/javascript">
    				function more(display, id){
    					var e=document.getElementsByTagName("p");
    					for(i=0; i<e.length; i++){
    						if(e[i].className=="hidden"&&(e[i].getAttribute("name")==id || id==""))
    							e[i].style.display = display==e[i].style.display?"none":display;
    					}
    				}
    				more("none","");
    			</script>
    			<p><a href="javascript:void(0)" onclick="more('block','p1')">More</a></p>
    			</span>
    		</td>
    	</tr>
    </table>
    <table width="600" border="0" cellpadding="5" cellspacing="0" bgcolor="#FFFFFF">
        <tr>
    		<td>
    			<p>This is the second.</p>
    			<span class="style2">
    			<script type="text/javascript">
    				function more(display, id){
    					var e=document.getElementsByTagName("p");
    					for(i=0; i<e.length; i++){
    						if(e[i].className=="hidden"&&(e[i].getAttribute("name")==id || id==""))
    							e[i].style.display = display==e[i].style.display?"none":display;
    					}
    				}
    				more("none","");
    			</script>
    			<p><a href="javascript:void(0)" onclick="more('block','p2')">More</a></p>
    			</span>
    		</td>
    	</tr>
    </table>
    <table width="600" border="0" cellpadding="5" cellspacing="0" bgcolor="#FFFFFF">
        <tr>
    		<td>
    			<p>This is the third </p>
    			<span class="style2">
    			<p class="hidden" name="p3"> that will be shown.</p>
    			<script type="text/javascript">
    				function more(display, id){
    					var e=document.getElementsByTagName("p");
    					for(i=0; i<e.length; i++){
    						if(e[i].className=="hidden"&&(e[i].getAttribute("name")==id || id==""))
    							e[i].style.display = display==e[i].style.display?"none":display;
    					}
    				}
    				more("none","");
    			</script>
    			<p><a href="javascript:void(0)" onclick="more('block','p3')">More</a></p>
    			</span>
    		</td>
    	</tr>
    </table>

    Needless though it is to say, some improvements need to be made:
    • I'll completely ignore the tables for layout issue, as that can be covered on another day
    • The span doesn't work as intended because it's not allowed to wrap block-level elements such as paragraphs
    • The same identical function definitely does not need to have multiple identical versions of itself being present
    • The id attributes won't be needed for the hidden elements, because other forms of referencing can be performed instead
    • Hiding the content by default causes the text to be unavailable to people without javascript


    So what is wanted instead. I won't remove the tables, but I will use only one table inside of which will be the paragraphs. If you want a separate table around each individual paragraph then that is an easy change for you to make.

    What I intend to have is one paragraph per result, with the more text inside that same paragraph, but surrounded by a classed span so that the script will know to look for and process that text.

    Code html4strict:
    <p> This is the first <span class="more"> that will be shown.</span></p>

    The class name allows our script to look through the page for all spans that have the "more" class

    Code javascript:
    var spans = document.getElementsByTagName('span'),
        spansLen = spans.length,
        i;
    for (i = 0; i < spansLen; i += 1) {
        if (spans[i].className === 'more') {
    	    addMore(spans[i]);
    	}
    }

    There are fancier ways to get elements by their class name, but for now such techniques aren't required.

    When each span with the "more" class name has been found, we add a more link to the page, with its own class name so that we can refer to it from css as well.

    Code javascript:
    function addMore(el) {
    	var a = document.createElement('a');
    	a.href = '#';
    	a.className = 'showMore';
    	if (el.nextSibling) {
    	    el.parentNode.insertBefore(a, el.nextSibling);
    	} else {
    		el.parentNode.appendChild(a);
    	}
    	hideMore.call(a);
    }

    Placing the more link inline with the text makes it a lot easier to show and hide the text. It's place after the text though so that if you want the more link to be shown on a separate line, you can use some simple css to change it to a block-level element instead.

    Code css:
    a.showMore {
    	display: block;
    }

    And finally, the showMore and hideMore functions update the more link, and show or hide the text that's in the span just before the more link itself.

    Code javascript:
    function showMore() {
    	this.innerHTML = 'Hide';
    	this.onclick = hideMore;
    	this.previousSibling.style.display = '';
    	return false;
    }
    function hideMore() {
    	var text = document.createTextNode('More');
    	this.innerHTML = 'More';
    	this.onclick = showMore;
    	this.previousSibling.style.display = 'none';
    	return false;
    }

    This now makes the php code a lot easier to write. Working backwards from our working showHide example, and using a couple of techniques to help manage the complexity, the php code would end up looking something like this:

    Code php:
    $thetask = '1';
    $sql = "SELECT * FROM cases WHERE task_id = '$thetask' AND company_id = '1'";
    $sqlresult = mysql_query($sql);
    $tasks = '';
    while($row = mysql_fetch_array($sqlresult)) {
    	$data = $row["body"];
    	$parts=explode('[splithere]', $data);
    	$tasks .= '<p>';
    	foreach ($parts as $i => $part) {
    		if ($i == 0) {
    			$tasks .= $part;
    		} else {
    			$tasks .= '<span class="more">' . $part . '</span>';
    		}
    	}
    	$tasks .= '</p>';
    }
    if ($tasks != '') {
        echo <<< EOT
    <table width="600" border="0" cellpadding="5" cellspacing="0" bgcolor="#FFFFFF">
      <tr>
        <td>$tasks</td>
      </tr>
    </table>
    <script type="text/javascript" src="showmore.js"> </script>
     
    EOT;
    }

    When you have the final html representation of what you need first, it can be easier to create the php code afterwards because then you know what you need to achieve.

    Here's the complete test html page for the showMore code.

    Code html4strict:
    <html>
    <head>
    <style type="text/css">
    .showMore {
    	display: block;
    }
    </style>
    </head>
    <body>
    <table width="600" border="0" cellpadding="5" cellspacing="0" bgcolor="#FFFFFF">
        <tr>
    		<td>
    			<p>This is the first <span class="more"> that will be shown.</span></p>
    			<p>This is the second.</p>
    			<p>This is the third <span class="more"> that will be shown.</span></p>
    		</td>
    	</tr>
    </table>
    <script type="text/javascript">
    var spans = document.getElementsByTagName('span'),
        spansLen = spans.length,
        i;
    for (i = 0; i < spansLen; i += 1) {
        if (spans[i].className === 'more') {
    	    addMore(spans[i]);
    	}
    }
    function addMore(el) {
    	var a = document.createElement('a');
    	a.href = '#';
    	a.className = 'showMore';
    	if (el.nextSibling) {
    	    el.parentNode.insertBefore(a, el.nextSibling);
    	} else {
    		el.parentNode.appendChild(a);
    	}
    	hideMore.call(a);
    }
    function showMore() {
    	this.innerHTML = 'Hide';
    	this.onclick = hideMore;
    	this.previousSibling.style.display = '';
    	return false;
    }
    function hideMore() {
    	var text = document.createTextNode('More');
    	this.innerHTML = 'More';
    	this.onclick = showMore;
    	this.previousSibling.style.display = 'none';
    	return false;
    }
    </script>
    </body>
    </html>

    Edit:

    Closing script tag added to the php code, and adding return false to the end of showMore and hideMore functions.
    Last edited by paul_wilkins; Mar 11, 2009 at 16:47.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,684
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    Does anybody have any criticism or commentary about the above?

    I would also like to hear thoughts about the idea of using pre-created webpage solution to guide changes to the server-side code.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  4. #4
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    257
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks very much for your incredibly detailed reply, I know I'm doing lots of things wrong but I don't have the time to re-do the whole thing from scratch!

    I'm having a few problems getting the code to all work together though aqnd tbh I really don't understand JS at all so am having problems seeing how to resolve it.

    Do you have a couple of minutes to throw together the code all together, integrating the PHP and the HTML so that I can see how it should all slot together? If not that's fine, I really appreciate the time you put in.

    Thanks

  5. #5
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,684
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    The last two pieces of code above are the php and the html.

    The php hasn't been tested because I don't have the resources available to hand, but it's pretty close to what's required.

    The html in the last piece of code is a complete working html page that demonstrates a working page using the previously described techniques.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  6. #6
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    257
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    thanks a lot for the quick reply. The issue I'm having is knowing how to insert the data from the database in place of the <p>This is the first <span class="more"> that will be shown.</span></p> code in the HTML code that you displayed. I can get the PHP to work and output the full piece of data (without the [splithere] code), and I can use the HTML to display your sample data, I just don't know how to merge the two together.

    Cheers

  7. #7
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,684
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    This code that I provided in the php part above does that.

    Code php:
    while($row = mysql_fetch_array($sqlresult)) {
        $data = $row["body"];
        $parts=explode('[splithere]', $data);
        $tasks .= '<p>';
        foreach ($parts as $i => $part) {
            if ($i == 0) {
                $tasks .= $part;
            } else {
                $tasks .= '<span class="more">' . $part . '</span>';
            }
        }
        $tasks .= '</p>';
    }

    After that the php code checks if $tasks contains anything, and if it does it then uses heredoc notation (that's the <<< EOT stuff) to display some html code with $tasks embedded inside that html.

    Heredoc is a more powerful php technique than doublequote strings.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  8. #8
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    257
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    The code I used is:

    Code:
    <?php
    $dbhostname = "blah";
    $dbdatabase = "blah";
    $dbusername = "blah";
    $dbpassword = "blah";
    $fgconnection = mysql_pconnect($dbhostname, $dbusername, $dbpassword) or die(mysql_error());
    @mysql_select_db($dbdatabase) or die("Database error (error 1)");
    ?>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Untitled Document</title>
    <style type="text/css">
    .showMore {
        display: block;
    }
    </style>
    </head>
    
    <body>
    <?php
    $thetask = '1';
    $sql = "SELECT * FROM updates WHERE parent_id = '$thetask' AND company_id = '1'";
    $sqlresult = mysql_query($sql);
    $tasks = '';
    while($row = mysql_fetch_array($sqlresult)) {
        $data = $row["body"];
        $parts=explode('[splithere]', $data);
        $tasks .= '<p>';
        foreach ($parts as $i => $part) {
            if ($i == 0) {
                $tasks .= $part;
            } else {
                $tasks .= '<span class="more">' . $part . '</span>';
            }
        }
        $tasks .= '</p>';
    }
    if ($tasks != '') {
        echo <<< EOT
    <table width="600" border="0" cellpadding="5" cellspacing="0" bgcolor="#FFFFFF">
      <tr>
        <td>$tasks</td>
      </tr>
    </table>
    <script type="text/javascript" src="showmore.js">
      
    EOT;
    }
    ?>
    <script type="text/javascript">
    var spans = document.getElementsByTagName('span'),
        spansLen = spans.length,
        i;
    for (i = 0; i < spansLen; i += 1) {
        if (spans[i].className === 'more') {
            addMore(spans[i]);
        }
    }
    function addMore(el) {
        var a = document.createElement('a');
        a.href = '#';
        a.className = 'showMore';
        if (el.nextSibling) {
            el.parentNode.insertBefore(a, el.nextSibling);
        } else {
            el.parentNode.appendChild(a);
        }
        hideMore.call(a);
    }
    function showMore() {
        this.innerHTML = 'Hide';
        this.onclick = hideMore;
        this.previousSibling.style.display = '';
    }
    function hideMore() {
        var text = document.createTextNode('More');
        this.innerHTML = 'More';
        this.onclick = showMore;
        this.previousSibling.style.display = 'none';
    }
    </script>
    </body>
    </html>
    It is correctly returning the two results (content is the same but that's fine), however it is ignoring the [splithere] text, which appears in the database as 'this is [splithere] some mroe text', so the whole result is being printed without the mode/hide buttons.

    Any idea what I'm doing wrong?

    Cheers
    Last edited by grandad; Mar 11, 2009 at 05:56.

  9. #9
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,684
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    Look at the web page source code

    Code htmlstrict:
    ...
    </table> 
    <script type="text/javascript" src="showmore.js"> 
      <script type="text/javascript">
    var spans = document.getElementsByTagName('span'),
    ...

    You need to make a choice. Either have the script code inline in the page, or load it in from an external source. I suggest the latter choice.

    Save the javascript code out to an external file called showmore.js (or some other name of your liking) and use that external file instead of inline script code.

    By the way, I just noticed something that needs to be fixed in that php code. The script reference needs to have a closing script tag.

    Code html4strict:
    <script type="text/javascript" src="showmore.js"> </script>
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  10. #10
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    257
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm an idiot! It's working nicely now, thanks so much for your help.

  11. #11
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,684
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    You're welcome. Hey by the way - did you noticed that in moving the css and javascript out away from the html code, that it's easier to read and understand the source code?

    And, what do you think about building the content in php first before wrapping it in other code. Is it easier to read and understand than the original technique where you try to echo the page sequentially line by line?
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  12. #12
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    257
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes it did seem better to split it into separate files, but is there any advantage to doing this apart from it being easier to read?

    Also, I noticed that when clicking the link to show/hide text it jumps back to the top of the page, so if I click a link after scrolling down it jumps to the top. Is there any way of avoiding this?

  13. #13
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,684
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by grandad View Post
    Yes it did seem better to split it into separate files, but is there any advantage to doing this apart from it being easier to read?

    Apart from the html and javascript code being easier to read, it allows the code to be more easily reused on other pages.

    Another advantage is that after the first visit to the page, other revisits tend to cache the javascript file that was downloaded the first time.
    Quote Originally Posted by grandad View Post
    Also, I noticed that when clicking the link to show/hide text it jumps back to the top of the page, so if I click a link after scrolling down it jumps to the top. Is there any way of avoiding this?
    Yes there is. It can be achieved by returning false from the end of the event functions, showMore and hideMore. This prevents the default behaviour, of following the fake link, from occurring.

    I'll update the original code as well for when others want to use it too.

    Code javascript:
    function showMore() {
        this.innerHTML = 'Hide';
        this.onclick = hideMore;
        this.previousSibling.style.display = '';
        return false;
    }
    function hideMore() {
        var text = document.createTextNode('More');
        this.innerHTML = 'More';
        this.onclick = showMore;
        this.previousSibling.style.display = 'none';
        return false;
    }
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  14. #14
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    257
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sorry to re-open an old case, however I'm come up against another issue.

    I'm using the following code:

    Code:
    <?php
    $sql = "SELECT * FROM updates WHERE parent_id = '1' AND company_id = '4'";
    $sqlresult = mysql_query($sql);
    $tasks = '';
    while($row = mysql_fetch_array($sqlresult)) {
        $data = $row["body"];
        $parts=explode('[reply above this]', $data);
        $tasks .= '<p>';
        foreach ($parts as $i => $part) {
            if ($i == 0) {
                $tasks .= $part;
            } else {
                $tasks .= '<span class="more">' . $part . '</span>';
            }
        }
        $tasks .= '</p>';
    }
    if ($tasks != '') {
        echo <<< EOT
    <table width="600" border="0" cellpadding="5" cellspacing="0" bgcolor="#FFFFFF">
      <tr>
        <td>$tasks</td>
      </tr>
    </table>
    <script type="text/javascript" src="showmore.js"></script>
      
    EOT;
    }
    ?>
    This works just fine as long as all of the text in the body row is plain text, however when it contains HTML tags it fails to split it correctly, the full body shows even before I click the Show more link.

    Is there anything that can be done about this?

    Cheers

  15. #15
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,684
    Mentioned
    99 Post(s)
    Tagged
    4 Thread(s)
    What sort of html tags? An example of he html code that would go in the row would enable us to help you a lot better.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •