I am implementing a CSS read more / read less capability using http://codepen.io/Idered/pen/AeBgF as a starting point.
I modified it to work off <p>
tags vs. <li>
list items.
I can't get the code to work on my page at http://bit.ly/1L5vMm7
Here is my version of the CSS:
input.read-more-state {
display: none;
}
p.read-more-target {
font-size: 0;
max-height: 0;
opacity: 0;
transition: .25s ease;
}
input.read-more-state:checked ~ div.read-more-wrap p.read-more-target {
font-size: inherit;
max-height: 999em;
opacity: 1;
}
input.read-more-state ~ label.read-more-trigger:before {
content: 'Read more';
}
input.read-more-state:checked ~ label.read-more-trigger:before {
content: 'Read less';
}
label.read-more-trigger {
cursor: pointer;
display: inline-block;
}
Here is my HTML:
<input class="read-more-state" id="read-more-controller" type="checkbox">
<div class="read-more-wrap">
<p>Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
</div>
<label class="read-more-trigger" for="read-more-controller"></label>
Let me know if you can discover what is conflicting with the RM/RL utility.
How does the CodePen achieve this behavior?
All that the code in the demo does is modify the max-height
of the wrapper div
based on the check-box being checked or unchecked and whilst doing so also change the content of the label
.
Now lets have a look at the key individual selectors in CSS that help perform this:
.read-more-state:checked ~ .read-more-wrap .read-more-target
- This selector means that when an
input
with class = 'read-more-state'
is checked, select the elements with class = 'read-more-target'
which are present under a wrapper with class = 'read-more-wrap'
when the wrapper is also a sibling of the checkbox (the reference element).
.read-more-state ~ .read-more-trigger:before
- This is the one that populates the default text for the
label
. What it does is set the content
as "Show more" for the ::before
element of label
with class = 'read-more-trigger'
when the label
is also a sibling of the checkbox.
.read-more-state:checked ~ .read-more-trigger:before
- This is the one that modifies the text of the
label
when the checkbox is clicked. The selector means that when the input
with class = 'read-more-state'
is :checked
, set the content
of the label's before element as "Show less".
Also, note the for
attribute in the label
tag. The value of this field points to the id
of the input
tag and so whenever the label is clicked, the input
element's state gets toggled automatically. This will happen irrespective of where in DOM the label
and input
elements are.
.read-more-state {
display: none;
}
.read-more-target {
opacity: 0;
max-height: 0;
font-size: 0;
transition: .25s ease;
}
.read-more-state:checked ~ .read-more-wrap .read-more-target {
opacity: 1;
font-size: inherit;
max-height: 999em;
}
.read-more-state ~ .read-more-trigger:before {
content: 'Show more';
}
.read-more-state:checked ~ .read-more-trigger:before {
content: 'Show less';
}
.read-more-trigger {
cursor: pointer;
display: inline-block;
padding: 0 .5em;
color: #666;
font-size: .9em;
line-height: 2;
border: 1px solid #ddd;
border-radius: .25em;
}
/* Other style */
body {
padding: 2%;
}
p {
padding: 2%;
background: #fff9c6;
color: #c7b27e;
border: 1px solid #fce29f;
border-radius: .25em;
}
<div>
<input type="checkbox" class="read-more-state" id="post-1" />
<p class="read-more-wrap">Lorem ipsum dolor sit amet, consectetur adipisicing elit. <span class="read-more-target">Libero fuga facilis vel consectetur quos sapiente deleniti eveniet dolores tempore eos deserunt officia quis ab? Excepturi vero tempore minus beatae voluptatem!</span>
</p>
<label for="post-1" class="read-more-trigger"></label>
</div>
<div>
<input type="checkbox" class="read-more-state" id="post-2" />
<ul class="read-more-wrap">
<li>lorem</li>
<li>lorem 2</li>
<li class="read-more-target">lorem 3</li>
<li class="read-more-target">lorem 4</li>
</ul>
<label for="post-2" class="read-more-trigger"></label>
</div>
Why does my code not work?
CSS selectors can select a sibling element only when it is present after/below the reference element in the DOM. In your snippet, the div
which needs to be selected is present after the input
(which is the reference element and whose state triggers the action) and hence CSS is not able to select it. No selection results in no property change being applied.
input.read-more-state {
display: none;
}
p.read-more-target {
font-size: 0;
max-height: 0;
opacity: 0;
transition: .25s ease;
}
input.read-more-state:checked ~ div.read-more-wrap p.read-more-target {
font-size: inherit;
max-height: 999em;
opacity: 1;
}
input.read-more-state ~ label.read-more-trigger:before {
content: 'Read more';
}
input.read-more-state:checked ~ label.read-more-trigger:before {
content: 'Read less';
}
label.read-more-trigger {
cursor: pointer;
display: inline-block;
}
<div class="read-more-wrap">
<p>Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
</div>
<input class="read-more-state" id="read-more-controller" type="checkbox">
<label class="read-more-trigger" for="read-more-controller"></label>
How can I solve this problem?
Just move the input
tag to be above the wrapper div
tag whose max-height
needs to be changed when the label is clicked. Doing this would mean that the reference element is now above the element that needs to be selected and styled. Thus, CSS would be able to apply the max-height
properly and reveal the hidden contents.
input.read-more-state {
display: none;
}
p.read-more-target {
font-size: 0;
max-height: 0;
opacity: 0;
transition: .25s ease;
}
input.read-more-state:checked ~ div.read-more-wrap p.read-more-target {
font-size: inherit;
max-height: 999em;
opacity: 1;
}
input.read-more-state ~ label.read-more-trigger:before {
content: 'Read more';
}
input.read-more-state:checked ~ label.read-more-trigger:before {
content: 'Read less';
}
label.read-more-trigger {
cursor: pointer;
display: inline-block;
}
<input class="read-more-state" id="read-more-controller" type="checkbox">
<div class="read-more-wrap">
<p>Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
<p class="read-more-target">Lorem ipsum dolor sit amet...</p>
</div>
<label class="read-more-trigger" for="read-more-controller"></label>