Nathaniel Johnston
Nathaniel Johnston

Reputation: 11

How to move an HTML element from one position to another via JavaScript click event

I am wanting to move an element from one position to another, on a click event, but the animation is really janky.

You can view the prototype here.

link

My JS

// intialize the selected card as false
let selectedCard = false;
// find all the cards
let cards = document.querySelectorAll('.flex-item');
console.log(cards);
// grab the button
let button = document.querySelector('button')
// initialize the first card position
let firstCardPosition = '';
// intialize the selected card position
let selectedCardPosition = '';
// 
let isAnimating = false;

// Get the selected card position
function getSelectedCardPosition(card) {
  selectedCardPosition = card.getBoundingClientRect();
  console.log(selectedCardPosition);
};

// Get the first card position
function getFirstCardPosition(cards) {
  if (cards) {
    firstCardPosition = cards[0].getBoundingClientRect();    
  }
};

// Change the display of the card
function hideCards(card, interval) {
  isAnimating = true;
  setTimeout(function() {
    isAnimating = false;
    card.hidden = true;
  }, interval)
};

function showCards(card, interval) {
  isAnimating = true;
  setTimeout(function() {
    isAnimating = false;
    card.hidden = false;
  }, interval)
};


function fadeCards(cards) {
  cards.forEach(function(card) { 
    if (!card.selectedCard) {
      card.classList.remove('card-show');
      card.classList.add('card-hide');
      hideCards(card, 785);
    }
  });
};

function revealCards(cards) {
  cards.forEach((card) => {
    if (!card.selectedCard) {
      showCards(card, 0);
      card.classList.remove('card-hide');
      card.classList.add('card-show');
    }
  })
}

// Go through each card
// Add an attribute 'selectedCard'
// On first click, it is selected. We toggle it based on click
// If 'selectedCard' is true, than display the selected div - otherwise
// hide the div. 
cards.forEach(function(card) {

  // initialize the value to false
  card.selectedCard = false;

  // Grab the div that is the selected div.
  let showSelected = card.firstElementChild;

  // on click, do the things below. 
  card.addEventListener('click', function() {
    // if animating, get out of this function so no jankiness occurs
    if (isAnimating){
      return
    }
    // Toggle the value of the selected card
    card.selectedCard = !card.selectedCard;
    card.selectedCard ? showSelected.style.display = 'block' : showSelected.style.display = 'none';
    getSelectedCardPosition(card);
    getFirstCardPosition(cards)
    if (card.selectedCard) {
      fadeCards(cards); 
    } else {
      revealCards(cards);
    }
    moveToDestination(card, firstCardPosition, selectedCardPosition);
  });

});


function moveToDestination(card, firstCardPosition, selectedCardPosition) {
  const firstCardX = firstCardPosition.x;
  const firstCardY = firstCardPosition.y;
  let selectedCardX = selectedCardPosition.x;
  let selectedCardY = selectedCardPosition.y;

  let moveToXPosition = (selectedCardX - firstCardX) * -1;
  let moveToYPosition = (selectedCardY - firstCardY) * -1;


  let translateX = 'translateX' + '(' + moveToXPosition + 'px' + ')';
  let translateY = 'translateY' + '(' + moveToYPosition + 'px' + ')';
  console.log(translateX);
  console.log(translateY);
  card.animate(
    [
      // keyframes
      { transform: translateX },
      { transform: translateY }
    ], {
      duration: 800,
      easing: "ease-in-out"
    });
}

There are a few challenges here:

Am I trying to do something that just isn't going to work the way I intend it to or is my implementation poorly done?

Upvotes: 1

Views: 1587

Answers (1)

Faisal Rashid
Faisal Rashid

Reputation: 378

    cards.forEach(function(card) {

  // initialize the value to false
  card.selectedCard = false;

  // Grab the div that is the selected div.
  let showSelected = card.firstElementChild;

  // on click, do the things below. 
  card.addEventListener('click', function() {
    // if animating, get out of this function so no jankiness occurs
    if (isAnimating){
      return
    }
    // Toggle the value of the selected card
    card.selectedCard = !card.selectedCard;
    card.selectedCard ? showSelected.style.display = 'block' : showSelected.style.display = 'none';
    // only save the coordinates of the selected card if it's being selected and don't overwrite those when it's clicked again
    if (card.selectedCard)
      getSelectedCardPosition(card);
    getFirstCardPosition(cards) 
    console.log(card, selectedCardPosition);

   // Modifications to the method to add the animation onclick 
  if(card.selectedCard){ 
    card.style.position = 'fixed';
    // subtract 32px for padding
    card.style.top = selectedCardPosition.y - 32 + 'px'; 
    card.style.left = selectedCardPosition.x - 32 + 'px';
    setTimeout(function () {
    card.style.top = firstCardPosition.y - 32 + 'px';
    card.style.left = firstCardPosition.x - 32 + 'px';
    }, 1000)
  }
  // if the card is being unselected return it to it's previously saved coordinates
  else {
    card.style.top = selectedCardPosition.y - 32 + 'px'; 
    card.style.left = selectedCardPosition.x - 32 + 'px';
  }




    if (card.selectedCard) {
      fadeCards(cards); 
    } else {
      revealCards(cards);

      setTimeout(() => {
              card.style.position = 'relative';
      card.style.left = 'auto';
      card.style.top = 'auto';
      }, 500)



          moveToDestination(card, firstCardPosition, selectedCardPosition);

    }
  });

});

I have made a change to your onclick handler and made the position of the card fixed on click. I have also added a setTimeout function to make sure the animation occurs 1 second after the element is clicked. This will definitely need to be refactored. Otherwise, it will work.

Here's the modified pen.

https://codepen.io/faisalrashid/pen/zYOBVPg

Upvotes: 2

Related Questions