Is there a cleaner way of writing this jQuery code to swap images?

Viewed 35

I have this code that basically swaps an image every time the related checkbox is clicked. However, the jQuery code seems quite repetitive and I'd be interested to know how I could write this to be cleaner.

$(function() {

  $('#switch1').change(function(){
      if ($(this).is(':checked')) {
          $('#image1').attr('src','image1-swap-png');
        } else {
      $('#image1').attr('src','image1-swap-png');
        };
    });
  
  $('#switch2').change(function(){
      if ($(this).is(':checked')) {
          $('#image2').attr('src','image2-swap-png');
        } else {
      $('#image2').attr('src','image2-swap-png');
        };
    });
  
  $('#switch3').change(function(){
      if ($(this).is(':checked')) {
          $('#image3').attr('src','image3-swap-png');
        } else {
      $('#image3').attr('src','image3-swap-png');
        };
    });

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h3>First example</h3>
    <div>
      <input type="checkbox" id="switch1">
      <label>Checkbox</label>
    </div>
    <img id="image1" src="image1.png">
    
    <h3>Second example</h3>
    <div>
      <input type="checkbox" id="switch2">
      <label>Checkbox</label>
    </div>
    <img id="image2" src="image2.png">
    
    <h3>Third example</h3>
    <div>
      <input type="checkbox" id="switch3">
      <label>Checkbox</label>
    </div>
    <img id="image3" src="image3.png">

Thanks in advance

3 Answers

You can use jquery selectors attribute selector [] with ^= which will match attribute value starting with provided value.

Then find parent div with closest('div') and then next img element with .next('img') and update its src.

As per sample in your question there is an identical code inside if else block, so I haven't added if else.

// get all elements whose id starts with switch
$('[id^=switch]').change(function() {
  // get id of current input and remove switch. Rest part will be index
  let index = $(this).attr('id').replace('switch', '');

  // you can add $(this).is(':checked') if needed

  // find parent div and then next img element & set src
  $(this).closest('div').next('img').attr('src', 'image' + index + '-swap-png');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<h3>First example</h3>
<div>
  <input type="checkbox" id="switch1">
  <label>Checkbox</label>
</div>
<img id="image1" src="image1.png">

<h3>Second example</h3>
<div>
  <input type="checkbox" id="switch2">
  <label>Checkbox</label>
</div>
<img id="image2" src="image2.png">

<h3>Third example</h3>
<div>
  <input type="checkbox" id="switch3">
  <label>Checkbox</label>
</div>
<img id="image3" src="image3.png">

The technique you're looking for is called Don't Repeat Yourself, or DRY. To do apply that to the code you've shown you can amend the HTML to use a combination of common classes, to group elements by behaviour, and data attributes, to place custom metadata on an element.

From there you can use a single event handler for all elements, with the differences applied through the data attributes. Try this:

jQuery($ => {
  $('.switch').on('change', e => {
    let $image = $(e.target).closest('div').next('.image');
    $image.prop('src', $image.data(e.target.checked ? 'checked' : 'unchecked'));
  });
});
img { width: 50px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h3>First example</h3>
<div>
  <input type="checkbox" class="switch">
  <label>Checkbox</label>
</div>
<img class="image" src="https://i.imgur.com/dC9QaX4.png" data-checked="https://i.imgur.com/1uN7N7b.png" data-unchecked="https://i.imgur.com/dC9QaX4.png">

<h3>Second example</h3>
<div>
  <input type="checkbox" class="switch">
  <label>Checkbox</label>
</div>
<img class="image" src="https://i.imgur.com/dC9QaX4.png" data-checked="https://i.imgur.com/1uN7N7b.png" data-unchecked="https://i.imgur.com/dC9QaX4.png">

Store all the switch elements in an array.

const switches = [$('#switch1'), $('#switch2'), $('#switch3')];    

Iterate over the array. Use the index + 1 to get image number. Put the image number into the image id selector using backticks.

switches.forEach((switch, i) => {
   if (switch.is(':checked')) {
      $(`#image${i}`).attr('src',`image${i}e-swap-png`);
   } else {
      $(`#image${i}`).attr('src',`image${i}-swap-png`);
   };
})
Related