Slide To Submit 2024

by kthornbloom

HTML

<div class="s2s">
  <div class="s2s-submitting">
    Placing Your Order
  </div>
  <div id="s2s-live-region" aria-live="assertive"></div>
  <div class="s2s-message">
    Slide To Submit
  </div>
  <div class="s2s-message-anim"></div>
  <div class="s2s-green"></div>
  <button class="s2s-thumb" aria-label="To buy this item now, drag this button or tap the right arrow key three times for keyboard users.">
    <div class="s2s-icon"></div>
  </button>
</div>

CSS

:root {
     --thumbsize: 50px;
   }
   
  @keyframes fade {
     0% {background-position-x: 0}

     0% {background-position-x: -300px}
   }
   
   @keyframes spin {
     0% {transform: rotate(0deg)}
     100% {transform: rotate(360deg)}
   }

   .s2s {
     background: #434750;
     color: #fff;
     border: 1px solid #ccc;
     border-radius: var(--thumbsize);
     height: var(--thumbsize);
     position: relative;
     width: 300px;
     max-width: 100%;
   }

   .s2s-message {
     background: #434750;
     box-shadow: inset 0 0 20px #00000050;
     border-radius: var(--thumbsize);
     box-sizing: border-box;
     position: absolute;
     width: 100%;
     height: 100%;
     display: flex;
     align-items: center;
     justify-content: center;
     padding-left: 30px;
     position: absolute;
     right: 0;
     top: 0;
   }

   #s2s-live-region {
     position: absolute;
     height: 1px;
     width: 1px;
     overflow: hidden;
     clip: rect(1px, 1px, 1px, 1px);
     clip-path: inset(50%);
     white-space: nowrap;
   }

   @media (prefers-reduced-motion: no-preference) {
     .s2s-message-anim {
       width: 100%;
       height: 100%;
       border-radius: var(--thumbsize);
       position: absolute;
       background: linear-gradient(90deg, #43475000 20%, #43475080, #43475000 70%);
       animation: 1s fade infinite linear;
       left: 0;
       top: 0;
     }
   }

   .s2s-green {
     width: 100%;
     height: 100%;
     border-radius: var(--thumbsize);
     position: absolute;
     background: linear-gradient(90deg, #2c681c, #284d1f);
     left: 0;
     top: 0;
     opacity: 0;
   }

   .s2s-thumb {
     width: calc(var(--thumbsize) - 4px);
     height: calc(var(--thumbsize) - 4px);
     margin: 2px;
     background: #3a7d19;
     position: absolute;
     outline: none;
     border-radius: 50px;
     outline: none;
     top: 0;
     left: 0;
     border: none;
     display: flex;
     align-items: center;
     justify-content: center;

    ...

JavaScript

const dragHandle = document.querySelector('.s2s-thumb');
handleDrag(dragHandle, handleDragSubmit);
keyboardDrag(dragHandle, handleDragSubmit);

function handleDragSubmit() {
  console.log('submit');
  const processingMessage = document.querySelector('.s2s-submitting');
  processingMessage.classList.add('s2s-submitting-active')
}

function keyboardDrag(element, callback) {
  var rightKeyTapCount = 0;
  const parentWidth = element.parentNode.clientWidth - 50;
  var liveRegion = document.getElementById('s2s-live-region');

  element.addEventListener('keydown', function(event) {
    if (event.key === 'ArrowRight') {
      rightKeyTapCount++
      liveRegion.innerHTML = '';
      var plural = 's';

      if (rightKeyTapCount >= 3) {
        element.style.left = parentWidth + 'px';
        var newText = document.createTextNode('Submitting Order. Please stay on this page.');
        liveRegion.appendChild(newText);
        callback();

      } else {
        if (rightKeyTapCount === 2) {
          plural = '';
        }
        var newText = document.createTextNode('Tap the right arrow ' + (3 - rightKeyTapCount) + ' more time' + plural + ' to buy now.');
        liveRegion.appendChild(newText);
        element.style.left = (parentWidth / 3) * rightKeyTapCount + 'px';
      }
    }
  });

}

function handleDrag(element, callback) {
  let isDragging = false;
  let startX = 0;
  let offsetX = 0;
  const parentWidth = element.parentNode.clientWidth - 50,
    icon = element.querySelector('.s2s-icon'),
    greenbg = element.parentNode.querySelector('.s2s-green');

  // Function to update the element's position, rotation, and height
  function updatePositionRotationAndHeight(x) {
    offsetX = x - startX;
    if (offsetX < 0) offsetX = 0; // Prevent dragging outside left edge
    if (offsetX > parentWidth) offsetX = parentWidth; // Prevent dragging outside right edge

    // Calculate percentage of movement within the draggable range
    const percentage = offsetX /...