4

I've set my page to create a large drop-cap letter on the first paragraph of content. This works as expected, but when I have another div with a specified class before my affected elements, it makes the drop cap disappear.

See examples...
Correct display: http://jsfiddle.net/KPqgQ/
Incorrect display: http://jsfiddle.net/d73u9/

I have the following code in my page:

<section id="review">
  <div class="content_right"></div>
  <div class="content_left">
    <p>text</p>
    <p>text</p>
  </div>
  <div class="content_left">
    <p>text</p>
    <p>text</p>
  </div>
</section>

With the following CSS:

   #review .content_left:first-of-type p:first-of-type{
        padding-top:10px;
    }

    #review .content_left:first-of-type p:first-of-type:first-letter{
        float: left;
        line-height:90%;
        padding-right: 15px;
        padding-bottom: 10px;

        font-family: "Georgia",_serif;
        font-weight: bold;
        font-size: 60px;
        color:black;
    }

to create a drop-cap large first letter.

This code works when I remove the "content_right" div, but with it there, the CSS isn't working. I thought I had the correct order regarding declarations, but I must be missing something.

Does anyone know why this isn't working as intended?

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Yazmin
  • 1,154
  • 5
  • 17
  • 34

2 Answers2

6

This is working as intended as the :first-of-type selector actually selects the first of a specific type of element (section, div, span etc.) and doesn't actually select the first of a type of a class.

Alternatives

The '~' Fix

The tilde '~' workaround mentioned here.

/* Targets all paragraphs */
p 
{
        display: block;
        clear: both;
}

/* Targets all paragraphs within divs in #review */
#review div p
{ 
        padding-top:10px;
}

/* Targets the first letter within each paragraph */
#review div p:first-letter{
        float: left;
        line-height:90%;
        padding-right: 15px;
        padding-bottom: 10px;
        font-family: "Georgia",_serif;
        font-weight: bold;
        font-size: 60px;
        color:black;
}    

/* Removes the padding from all except the first paragraphs */
#review > div ~ div > p ~ p, 
#review > .content_left ~ .content_left p:first-of-type 
{
        padding-top: 0px;  
}

/* Removes the first letter stylings of the non-first paragraphs within 
   .content_left classes */
#review > .content_left ~ .content_left p:first-of-type:first-letter, 
#review > div ~ div > p ~ p:first-letter 
{
        float: none;
        line-height:90%;
        padding-right: 0px;
        padding-bottom: 0px;
        font-family: "Georgia",_serif;
        font-weight: normal;
        font-size: 12px; 
}

Example using '~' Method

(Thanks to BoltClock, for helping me along with this and it is his method after-all.)


Switching div Order

Moving the .content_right div behind the .content_left sections, as they are floats and you could still maintain the same layout.

Code:

<section id="review">
    <div class="content_left">
        <p>text</p>
        <p>text</p>
     </div>
     <div class="content_left">
        <p>text</p>
        <p>text</p>
     </div>
     <div class="content_right"></div>
</section>​ 

Example of switching order:


Using the :nth-of-type selector

Using the :nth-of-type selector to select the proper div.

Code:

#review .content_left:nth-of-type(2) :first-child
{
    padding-top:10px;
}

#review .content_left:nth-of-type(2) :first-child:first-letter
{
    float: left;
    line-height:90%;
    padding-right: 15px;
    padding-bottom: 10px;
    font-family: "Georgia",_serif;
    font-weight: bold;
    font-size: 60px;
    color:black;
}  

Example using :nth-of-type

Community
  • 1
  • 1
Rion Williams
  • 74,820
  • 37
  • 200
  • 327
  • You may want to provide an example of how my `~` solution can be applied here :) – BoltClock Dec 28 '12 at 21:58
  • @BoltClock, I started working on one and simply didn't have the time to make one, at least not in the amount of time that I wanted to spend on it :) – Rion Williams Dec 28 '12 at 22:00
  • Well, I can always put one together and post it separately. Although, there seem to be a lot of rules that need undoing which will be a pain... – BoltClock Dec 28 '12 at 22:08
  • Working on one currently - I feel like I am close. I have the first one selected, but the first paragraph of the second class still gets hit. http://jsfiddle.net/d73u9/4/ – Rion Williams Dec 28 '12 at 22:09
  • Ah, you don't have to repeat it for `p` because the element type is `p`, which means `:first-of-type:first-letter` will work as expected. The override that you need to do is with a selector `.content_left ~ .content_left p:first-of-type:first-letter`, which is the tedious part because there are a number of declarations which need overriding. It's getting late over here so I'm not into thinking off the top of my head right now though :P – BoltClock Dec 28 '12 at 22:11
  • I think that I managed to figure it out, I am sure that there is a great deal of redundancy, but "Hey - it looks good." :) http://jsfiddle.net/d73u9/7/ – Rion Williams Dec 28 '12 at 22:18
  • I posted my own answer too :) – BoltClock Dec 28 '12 at 22:36
  • By the way, I have another answer [here](http://stackoverflow.com/questions/2717480/css-selector-for-first-element-with-class/8539107#8539107) that goes into *even more* detail on why `:first-of-type` doesn't work for matching by class name. – BoltClock Dec 28 '12 at 22:40
  • Thank you guys, for the explanations and alternatives. I was leaning towards the idea that it was affecting the element and not the class, but I guess was too exhausted to communicate that in my initial description. I've decided to go with switching the div order for now. For SEO reasons pertaining to the site, that particular div should really come after the other ones. :) Thanks again! – Yazmin Dec 29 '12 at 21:26
2

Here's my take on the ~ workaround. I thought I'd chip in since Rion Williams was kind enough to link to my description of the workaround from his answer (which also does a good job explaining why your code doesn't work as expected):

/* The first p in all .content-left elements */
#review .content_left p:first-of-type {
    padding-top: 10px;
}

/* 
 * Undo the above style for the first p in subsequent .content-left elements.
 */
#review .content_left ~ .content_left p:first-of-type {
    padding-top: 0px;
}

/* The first letter in the first p in all .content-left elements */
#review .content_left p:first-of-type:first-letter {
    float: left;
    line-height: 90%;
    padding-right: 15px;
    padding-bottom: 10px;
    font-family: "Georgia", _serif;
    font-weight: bold;
    font-size: 60px;
    color: black;
}

/* 
 * Undo the above styles for the first letter in the first p
 * in subsequent .content-left elements.
 * 
 * I'm just using the initial values for every property here; you'll want to
 * adjust their values as necessary.
 */
#review .content_left ~ .content_left p:first-of-type:first-letter {
    float: none;
    line-height: normal;
    padding-right: 0px;
    padding-bottom: 0px;
    font-family: inherit;
    font-weight: normal;
    font-size: medium;
    color: inherit;
}

jsFiddle preview

Depending on your layout, you may need to repeat this for certain other classes too. Unfortunately, since CSS doesn't yet provide a selector that matches the first element of a certain class, you'll have to make do with overriding rules for now.

Community
  • 1
  • 1
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356