23

I want to zoom image with only CSS. The code below zooms the image when the left button of the mouse is kept pressed but I want to zoom in and out with a mouse click. How can I achieve that?

.container img {
  transition: transform 0.25s ease;
  cursor: zoom-in;
}

.container img:active {
  -webkit-transform: scale(2);
  transform: scale(2);
  cursor: zoom-out;
}
Nhan
  • 3,595
  • 6
  • 30
  • 38
iwsnmw
  • 602
  • 1
  • 6
  • 11
  • 2
    bunch of interesting answers here: [http://stackoverflow.com/questions/13630229/can-i-have-an-onclick-effect-in-css](http://stackoverflow.com/questions/13630229/can-i-have-an-onclick-effect-in-css) – Anthony Peruzzo Oct 04 '16 at 18:13

5 Answers5

40

Let's use a trick here, an input checkbox:

input[type=checkbox] {
  display: none;
}

.container img {
  margin: 100px;
  transition: transform 0.25s ease;
  cursor: zoom-in;
}

input[type=checkbox]:checked ~ label > img {
  transform: scale(2);
  cursor: zoom-out;
}
<div class="container">
  <input type="checkbox" id="zoomCheck">
  <label for="zoomCheck">
    <img src="https://via.placeholder.com/200">
  </label>
</div>
sambua
  • 2,274
  • 3
  • 22
  • 20
Nhan
  • 3,595
  • 6
  • 30
  • 38
16

Building on @Nhan answer: https://stackoverflow.com/a/39859268/661872

Shorter, scoped and does not require tracking ids for multiple elements.

.click-zoom input[type=checkbox] {
  display: none
}

.click-zoom img {
  margin: 100px;
  transition: transform 0.25s ease;
  cursor: zoom-in
}

.click-zoom input[type=checkbox]:checked~img {
  transform: scale(2);
  cursor: zoom-out
}
<div class="click-zoom">
  <label>
    <input type="checkbox">
    <img src="https://via.placeholder.com/200">
  </label>
</div>

<div class="click-zoom">
  <label>
    <input type="checkbox">
    <img src="https://via.placeholder.com/200">
  </label>
</div>
sambua
  • 2,274
  • 3
  • 22
  • 20
Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106
  • This is the neatest trick I've stumbled on for a long time. You can even set the img tag width as you see fit and it still works like a charm. A real keeper, doing so much with so little code. Hats off ! – Countzero Nov 23 '21 at 11:02
3

4 Ways to Add Click Events with Only CSS Pseudo-Selectors

Note: I'll be using the word target when referring to the element we want to manipulate and trigger as the element we are using to manipulate target.

:checked

Use checkboxes or radios and :checked to determine or cause a target's state and/or to take action.

Trigger

<label>
<input type="checkbox">
 <!--or--> 
<input type="radio">

Conditions

  • Requires that the target must be:
    • A sibling that follows the trigger or...
    • ...a descendant of the trigger.

Note

  • Hide the actual <checkbox> with display:none
  • Ensure that the <checkbox> has an id and that the <label> has a for attribute with a value matching the id of the <checkbox>
  • This is dependant upon the target being a sibling that follows the trigger or the target as a descendant. Therefore be aware that you'll most likely use these selector combinators: ~, +, >.

HTML

<label for='chx'>CHX</label>
<input id='chx' type="checkbox">

<div>TARGET</div>

CSS

#chx:checked + div {...

:target

Use an <a>nchor and apply the :target pseudo-selector on the target element.

Trigger

<a href=""></a>

Conditions

  • Assign an id to the target.
  • Assign that same id to the <a> href attribute preceding with a hash #

HTML

<a href='#target'>A</a>

<div id='target'>TARGET</div>

CSS

#target:target {...

:focus

The trigger element must be either an <input> type or have the attribute tabindex in order to use :focus.

Trigger

<div tabindex='0'>ANY INPUT OR USE TABINDEX</div>

Conditions

  • Target must a sibling that is located after the trigger or *target must be a descendant of the trigger.
  • State or effect will persist until user clicks elsewhere thereafter a blur or unfocus event will occur.

HTML

<nav tabindex='0'>
  <a href='#/'>TARGET</a>
  <a href='#/'>TARGET</a>
  <a href='#/'>TARGET</a>
</nav>

CSS

 nav:focus ~ a {...

`:active` -

This is a hack that cleverly exploits the transition-delay property in order to actually have a persistent state achieved with no script.

Trigger

<a href='#/'>A</a>

Conditions

  • Target must a sibling that is located after the trigger or *target must be a descendant of the trigger.
  • There must be a transition assigned to the target twice.
    • The first one to represent the persistent state.
    • The second one to represent the normal state.

HTML

<a href="#/">A</a>

<div class='target'>TARGET</div>

CSS

.target {
    opacity: 1;
    transition: all 0s 9999999s;
 }

 a:active ~ .target {
     opacity: 0;
     transition: all 0s;
 }

Wacked looking, right? Under normal circumstances, if your trigger had the :active pseudo-selector, we are able to manipulate the target upon keydown. That means our active state is actually active as long as you keep your finger on the button...that's crappy and useless, I mean what are you expected to do to make .active to be useful? Maybe a paperweight and some rubber bands to keep a steady and persistent pressure on the button?

We will leave .active the way it is: lame and useless. Instead:

  1. Make a ruleset for target under normal circumstances. In the example above it's opacity:1.
  2. Next we add a transition: ...ok then... all which works, next is 0s ...ok so this transition isn't going to be seen it's duration is 0 seconds, and finally... 9999999s ...116 days delay? We'll come back to that, we will continue onto the next rulesets...
  3. These rulesets declare what happens to target under the influence of trigger:active. As you can see that it just does what it normally does, which is onkeydown target will become invisible in 0 seconds. Now once the user keys up, target is visible again...no *target's * new state of opacity:0 is persistent! No paperweight, technology has come a long way.
  4. The target is still actually going to revert back to it's normal state, because :active is too lazy and feeble to work without rubber bands and paperweights. The persistent state is perceived and not real because target is still leaving the state brought on by :active which will be about 116 days before that will happen. ;)

This Snippet features the 4 ways previously mentioned. I'm aware that the OP requested zoom (which is featured therein), but thought it would be to repetitive and boring, so I added different effects as well as zooming.

###SNIPPET

a {
  text-decoration: none;
  padding: 5px 10px;
  border:1px solid red;
  margin: 10px 0;
  display: inline-block;
}
label {
  cursor: pointer;
  padding: 5px 10px;
  border: 1px solid blue;
  margin: 10px 0;
  display:inline-block;
}
button {
  cursor:pointer;
  padding: 5px 10px;
  border: grey;
  font:inherit;
  display:inline-block;
}
img#img {
  width: 384px;
  height: 384px;
  display: block;
  object-fit: contain;
  margin: 10px auto;
  transition: width 3s height 3s ease-in;
  opacity: 1;
  transition: opacity 1s 99999999s;
}
#zoomIn,
#zoomOut,
#spin {
  display: none;
  padding: 0 5px;
}
#zoomOut:checked + img#img {
  width: 128px;
  height: 128px;
  transition: all 3s ease-out;
}
#zoomIn:checked + img#img {
  width: 512px;
  height: 512px;
  transition: all 3s ease-in-out;
}
  
#spin:checked ~ img#img {
  transform: rotate(1440deg);
}
img#img:target {
  box-shadow: 0px 8px 6px 3px rgba(50, 50, 50, 0.75);
}
a.out:focus ~ img#img {
  opacity: 0;
  transition: opacity 1s;
}
a.in:active ~ img#img {
  opacity: 1;
  transition: opacity 1s;
}
.grey:focus ~ img#img {
  filter: grayscale(100%);
}
<a href='#/' class='out'>FadeouT</a><a href='#/' class='in'>FadeiN</a>

<a href='#img'>ShadoW</a>

<br/><button class='grey' tabindex='0'>GreyscalE</button><br/>

<label for='spin'>SpiN</label>
<input type='checkbox' id='spin'>

<label for='zoomIn'>ZoomiN</label>
<input type='radio' id='zoomIn' name='zoom'>

<label for='zoomOut'>ZoomouT</label>
<input type='radio' id='zoomOut' name='zoom'>

<img id='img' src='https://i.ibb.co/5LPXSfn/Lenna-test-image.png'>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
1
.container img {
  margin: 100px;
  transition: transform 0.25s ease;
  cursor: zoom-in;
}

input[type=checkbox]:checked ~ label > img {
  transform: scale(2);
  cursor: zoom-out;
}
<div class="container">
  <input type="checkbox" id="zoomCheck">
  <label for="zoomCheck">
    <img src="https://via.placeholder.com/200">
  </label>
</div>
sambua
  • 2,274
  • 3
  • 22
  • 20
  • I wish there was some explanation for what the code is doing. But this seems like the most elegant approach. – GaetaWoo Apr 27 '22 at 12:37
0
    <html>
   <head>
      <title>Image Zoom</title>
      <style type="text/css">
         #imagediv {
         margin:0 auto;
         height:400px;
         width:400px;
         overflow:hidden;
         }
         img {
         position: relative;
         left: 50%;
         top: 50%;
         }
      </style>
   </head>
   <body>
      <input type="button" value ="-" onclick="zoom(0.9)"/>
      <input type="button" value ="+" onclick="zoom(1.1)"/>
      <div id="imagediv">
         <img id="pic" src=""/>
      </div>
      <script type="text/javascript" language="javascript">
         window.onload = function(){zoom(1)}
         function zoom(zm) {
             img=document.getElementById("pic")
             wid=img.width
             ht=img.height
             img.style.width=(wid*zm)+"px"
             img.style.height=(ht*zm)+"px"
             img.style.marginLeft = -(img.width/2) + "px";
             img.style.marginTop = -(img.height/2) + "px";
         }
      </script>
   </body>
</html>
  • 1
    Gajal try to explain your answer in more detail rather than just posting the code. Please also indicate what you have changed or added. – Rarblack Dec 07 '18 at 06:32