Add a Class to container element in first block in Area

Permalink 1 user found helpful
I have an Area which contains several blocks of a single, new custom block type. The blocks generate simple output along the lines of:
> Heading
> Description
> "Read More" link
This is an HTML5 site I'm building and each block is wrapped in a <section> element generated in the view.php file for the custom block. All well so far....

However, on one page type these blocks go on, I need to add a class of "first" to the first <section>, which equates to the first Block rendered within the area.

How do I ascertain the first block rendered in an area and modify its HTML to add 'class="first"' to the section tag?

This may be a dumb question, but this is my first build using C5...

GavMurphy
View Replies: View Best Answer
GavMurphy replied on at Permalink Reply
GavMurphy
Oh, and I know from a CSS point of view I can identify the first <section>, but I need to add this attribute into the HTML.
GavMurphy replied on at Permalink Reply
GavMurphy
Does anyone know if within the view.php file of my custom block, I can check to see if this particular instance of the block about to be rendered is the first block in the area on the given page?

If I can do this, then I can put a simple conditional line in the view.php code that adds the HTML around the fields to allow me to put in class="first" or similar for the first block.

I see from core concrete code that the Area code for display($c) loops through all of the blocks for the Area, but I don't want to be doing anything in there, so hoping I can intercept it at the view.php to add my extras.

Thanks.
boomgraphics replied on at Permalink Reply
boomgraphics
Hello, I don't know if you know this, but if you click on the block after you add it into your page while you are still in edit mode, you can then click on "Design", and then the tab "CSS". There you can add your page specific class or id. What this does is add a div around the block, with your custom class.

Now, option two. You can set the area to recognize which page id it is in like so (x=your page ID):
<section class="<?php if ($c->getCollectionID() == x){ echo 'last'; } ?>">
<p>content</p>
</section>


Option three, you simply tell concrete5 to get the page handle and add it as a class.
Then you can style that section per page in your css:
<section class="<?php echo $c->getCollectionHandle(); ?>">
<p>content</p>
</section>


Option four, you can determine all of these things at once, plus only do it if there is a block there:
$pageHandle = $c->getCollectionHandle();
$pageID = $c->getCollectionID(); 
$a = new Area('Header');
 $a->display($c);
 if (!$a->getTotalBlocksInArea($c) > 0) {
echo '<section class="' . $pageID . ' ' . $pageHandle . '">';
}


I hope I could help! :-D

EDIT: You can also do this:
$inlineJS = '<script type="text/javascript">$(document).ready(function() {$("#sectionParent section:first-child").addClass("last");});</script>';
$pageID = $c->getCollectionID(); 
if ($pageID == x) {
echo $inlineJS;
} else {
//do something else here if page ID isn't right.
}
boomgraphics replied on at Permalink Reply
boomgraphics
I thought of another thing you could do which is better. In this one you check for a page attribute, and do something if it exists:
$last = if ($c->getCollectionAttributeValue('last')){
echo 'class="last"';
}
<section <?php $last ?>>
<p>content</p>
</section>


That way you can create the page attributes in the backend, and then for the page you want to echo the class="last" bit, you simply add the attribute to that particular page.

:-D
GavMurphy replied on at Permalink Reply
GavMurphy
Thanks for the detailed response, but I think it doesn't quite hit the nail on the head.

Each of my blocks contains a Heading, Description, Link.

In the view.php I have:

$linkedPage = $this->controller->getLinkedPage();
$nh = Loader::helper('navigation');
if ($linkedPage) {
   $pageURL = $nh->getLinkToCollection($linkedPage);
   $content = "{$detail}&nbsp;<a href=\"{$pageURL}\" class=\"more\">More&nbsp;&raquo;</a>";
} else {
   $content = $detail;
}
echo "<section>\n";
echo "<h3>{$heading}</h3>\n";
echo "<p>{$content}</p>\n";
echo "</section>\n";


So each block comes out as a fully formed section set.

When my page renders them, I just want to add class="first" to the first block.

I don't want to add it to the view (as what if these blocks are pulled for display elsewhere), I want to add it only on a particular template where the HTML is specific.

I don't want the user going in and setting attributes on what should be determinable from the system, and also, what if they change the sort order of the blocks? They would have to remember to remove and re-set attributes. Likewise, having users edit CSS is not desirable in this instance.

Thanks for the detail, but I'm not sure it solves my problem...but I might be wrong!
boomgraphics replied on at Permalink Reply
boomgraphics
This goes into the theme files, not the block files.

If there is a section element underneath the parent element on a certain page id, this will add the class to the first section element it encounters. That means you don't have to worry about whether they are putting other blocks in front of your section block, or whether they change the blocks ordering.

The main problem is determining what page to do this on, and I selected the page id to check (if you know this ahead of time). But you can use a page attribute check to determine what page to activate the code snippet on. The above still applies: it won't matter whether they reorder blocks, or put different blocks everywhere. This will only select the first occurring section element inside the specified parent on the specified page ID.

//put this code into header
$inlineJS = '<script type="text/javascript">$(document).ready(function() {$("#sectionParent section:first-child").addClass("first");});</script>';
$pageID = $c->getCollectionID(); 
if ($pageID == x) {
echo $inlineJS;
}
GavMurphy replied on at Permalink Reply
GavMurphy
Thanks again - but I'd like a solution that didn't use javascript.

I'm guessing I can't work out the 1st, 2nd, 3rd, nth block in an area as it's prepared for rendering unless wanting to mess about with core Concrete?
boomgraphics replied on at Permalink Best Answer Reply
boomgraphics
Nope, see here:

http://www.concrete5.org/community/forums/customizing_c5/find-the-l...

Maybe one of their solutions will give you an idea?
boomgraphics replied on at Permalink Reply
boomgraphics
I have looked around, and I found a decent solution. :-D

In your header.php, include phpQuery file (see below for website) like so:
include('phpQuery-onefile.php');


In your editable area where you are going to place your section blocks, place this code:
//x == your page ID you want this to work on
        if ($c->getCollectionID() == x) {
            ob_start();
            $a = new Area('Header');
            $a->display($c);
            $get = ob_get_clean();
            $doc = phpQuery::newDocumentHTML($get);
            $doc['section']->filter(':first')->addClass('first');
            echo phpQuery::getDocument($doc->getDocumentID());
        } else {
            $a = new Area('Header');
            $a->display($c);
        }


I have tested it, and it works to add the class .first to the first <section> tag it encounters for the page ID you want it to work on in the editable area you specify (Header, in this case). Again, you could use the page attributes instead of the page ID for the if statement to minimize issues with deleting a page and trying to find its page ID again, but it IS all server side. No JS. :-D

phpQuery website:http://code.google.com/p/phpquery/...
MrNiceGaius replied on at Permalink Reply
MrNiceGaius
Thanks Boomgraphics more great info!
TheRealSean replied on at Permalink Reply
TheRealSean
I did something similar when including jquery but only wanted it included once, I used a global count, if the global variable was below or equal to one then echo your class

global $first;
$class($first<=1)?" class='first'":"";
$first++;


When I was searching for the solution last time Jordan pointed me towards a getTotalBlocksInArea function. I didnt fully understand then but I believe you can take the area "contain_sections" and use it within your controller, the new class will retain all the block information about that area. (im not certain about this its all theory)

Meaning you can then to loop over the blocks in the area, filtering by Handle to retrieve only the ones you need.
You could then add these to an array check the index and if [0]?
If you add it to the controller it could be run on a $obj->isFirst();

**edit just noticed its mentioned in option 4 from BoomGraphic
boomgraphics replied on at Permalink Reply
boomgraphics
Haha yes I mentioned it, but I haven't got a clue what you're talking about. :-D

All I know is that it works for that particular purpose.