Tips for ensuring that a CSS infinite animation slider functions on mobile devices

I'm having trouble getting my price ticker/slider to work properly on mobile devices. It works perfectly on desktop/laptop, but on mobile the ticker items just briefly appear and then disappear without sliding animation.

Here is a clip of the bug: Clip

Snippet provided below.

EDIT - Removing the display-flex from the .ticker-container resolves the issue, but causes the ticker items to go outside the div, resulting in the slider resetting before displaying all ticker items.

Below is an example of what I have implemented so far:

const tickerMap = {
  'bitcoin': 'BTC',
  'ethereum': 'ETH',
  'binancecoin': 'BNB',
  'bitcoin-cash': 'BCH',
  'litecoin': 'LTC',
  'ripple': 'XRP',
  'chainlink': 'LINK',
  'matic-network': 'MATIC',
  'cardano': 'ADA',
  'dogecoin': 'DOGE',
  'shiba-inu': 'SHIB',
  'solana': 'SOL',
  'pepe': 'PEPE',
  'avalanche-2': 'AVAX',
  'polkadot': 'DOT'
};


async function fetchCryptoPrices() {
  try {
const response = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum,ripple,solana,pepe,binancecoin,avalanche-2,chainlink,matic-network,bitcoin-cash,shiba-inu,litecoin,polkadot,cardano,dogecoin&vs_currencies=usd', {
  mode: "cors"
});

  //const responseData = '{"avalanche-2":{"usd":44.41},"binancecoin":{"usd":577.56},"bitcoin":{"usd":66572},"bitcoin-cash":{"usd":679.98},"cardano":{"usd":0.565032},"chainlink":{"usd":17.01},"dogecoin":{"usd":0.16979},"ethereum":{"usd":3239.5},"litecoin":{"usd":96.28},"matic-network":{"usd":0.867247},"pepe":{"usd":6.44e-06},"polkadot":{"usd":8.16},"ripple":{"usd":0.573485},"shiba-inu":{"usd":2.662e-05},"solana":{"usd":170.15}}'

   const responseData = await response.text();
    console.log('Response from server:', responseData);
   const data = JSON.parse(responseData);
  return data;
 } catch (error) {
  console.error('Error fetching cryptocurrency prices:', error);
   return null;
 }
}

 async function createTickerItems() {
 const ticker = document.getElementById('ticker');
 ticker.innerHTML = ''; // Clear previous ticker items
 const cryptoPrices = await fetchCryptoPrices();
 if (cryptoPrices) {
  Object.entries(cryptoPrices).forEach(([symbol, price]) => {
  const tickerItem = document.createElement('div');
  tickerItem.classList.add('ticker-item');
  // Get the ticker symbol from the mapping, defaulting to the full name if not found
  const tickerSymbol = tickerMap[symbol] || symbol.toUpperCase();
  // Check if the symbol is one that needs custom decimal places
  let formattedPrice;
  if (symbol === 'shiba-inu' || symbol === 'pepe') {
    // Format price for custom decimal places
    formattedPrice = `$${price.usd.toFixed(6)}`;
  } else {
    // Default formatting for other tokens
    formattedPrice = `$${price.usd.toFixed(2)}`;
  }
  tickerItem.textContent = `${tickerSymbol}: ${formattedPrice}`;
  ticker.appendChild(tickerItem);
  });
  }
 }
// Initialize ticker
createTickerItems();
    .ticker-container {
  overflow: hidden;
  background-color: #111113;
  border-top: 1px solid #222;
  border-bottom: 1px solid #222;
  color: #fff;
  padding: 6px;
  white-space: nowrap;
}

.ticker {
  animation: marquee 20s linear infinite;
  width: max-content;
}

.ticker-item {
  margin-right: 20px;
  background-color: #1a191f;
  border-radius: 12px;
  padding: 4px 12px;
  font-size: 14px;
  display: inline-block;
}

.ticker-item:last-of-type {
  background-color: blue;
}

.ticker-item:first-of-type {
  background-color: red;
}

@keyframes marquee {
  0% {
    transform:
  }
  100% {}
  from {
    transform: translateX(100dvw);
    /* Use 100% of the viewport, not 100% of the element -> avoid waiting to long if you have many elements */
  }
  to {
    transform: translateX(-100%);
  }
}

@media(max-width:998px) {
  .ticker {
    animation: marquee 20s linear infinite !important;
  }
  .ticker-item {
    margin-right: 10px !important;
  }
}
<div class="ticker-container">
  <div class="ticker" id="ticker">
    <!-- Ticker items will be inserted here dynamically -->
  </div>
</div>

Answer №1

If you find that removing the display-flex property resolves the issue, consider using the following code snippet which utilizes display:inline-block; and width: max-content; to ensure the content is displayed correctly.

const tickerMap = {
  'bitcoin': 'BTC',
  'ethereum': 'ETH',
  'binancecoin': 'BNB',
  'bitcoin-cash': 'BCH',
  'litecoin': 'LTC',
  'ripple': 'XRP',
  'chainlink': 'LINK',
  'matic-network': 'MATIC',
  'cardano': 'ADA',
  'dogecoin': 'DOGE',
  'shiba-inu': 'SHIB',
  'solana': 'SOL',
  'pepe': 'PEPE',
  'avalanche-2': 'AVAX',
  'polkadot': 'DOT'
};


async function fetchCryptoPrices() {
  try {
    const response = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum,ripple,solana,pepe,binancecoin,avalanche-2,chainlink,matic-network,bitcoin-cash,shiba-inu,litecoin,polkadot,cardano,dogecoin&vs_currencies=usd', {
      mode: "cors"
    });

    //const responseData = '{"avalanche-2":{"usd":44.41},"binancecoin":{"usd":577.56},"bitcoin":{"usd":66572},"bitcoin-cash":{"usd":679.98},"cardano":{"usd":0.565032},"chainlink":{"usd":17.01},"dogecoin":{"usd":0.16979},"ethereum":{"usd":3239.5},"litecoin":{"usd":96.28},"matic-network":{"usd":0.867247},"pepe":{"usd":6.44e-06},"polkadot":{"usd":8.16},"ripple":{"usd":0.573485},"shiba-inu":{"usd":2.662e-05},"solana":{"usd":170.15}}'

    const responseData = await response.text();
    console.log('Response from server:', responseData);
    const data = JSON.parse(responseData);
    return data;
  } catch (error) {
    console.error('Error fetching cryptocurrency prices:', error);
    return null;
  }
}

async function createTickerItems() {
  const cryptoPrices = await fetchCryptoPrices();
  if (cryptoPrices) {
    document.querySelectorAll(".ticker-item").forEach((element) => element.remove()); // Clear previous ticker items
    Object.entries(cryptoPrices).forEach(([symbol, price]) => {
      const tickerItem = document.createElement('div');
      tickerItem.classList.add('ticker-item');
      // Get the ticker symbol from the mapping, defaulting to the full name if not found
      const tickerSymbol = tickerMap[symbol] || symbol.toUpperCase();
      // Check if the symbol is one that needs custom decimal places
      let formattedPrice;
      if (symbol === 'shiba-inu' || symbol === 'pepe') {
        // Format price for custom decimal places
        formattedPrice = `$${price.usd.toFixed(6)}`;
      } else {
        // Default formatting for other tokens
        formattedPrice = `$${price.usd.toFixed(2)}`;
      }
      tickerItem.textContent = `${tickerSymbol}: ${formattedPrice}`;

      const ticker = document.getElementById('ticker');
      ticker.appendChild(tickerItem);
    });
  }
}
// Initialize ticker
createTickerItems();
.ticker-container {
  overflow: hidden;
  background-color: #111113;
  border-top: 1px solid #222;
  border-bottom: 1px solid #222;
  color: #fff;
  padding: 6px;
  white-space: nowrap;
}

.ticker {
  animation: marquee 20s linear infinite;
  width: max-content;
}

.ticker-item {
  margin-right: 20px;
  background-color: #1a191f;
  border-radius: 12px;
  padding: 4px 12px;
  font-size: 14px;
  display: inline-block;
}

.ticker-item:last-of-type {
  background-color: blue;
}

.ticker-item:first-of-type {
  background-color: red;
}

@keyframes marquee {
  0% {
    transform:
  }
  100% {}
  from {
    transform: translateX(100dvw);
    /* Use 100% of the viewport, not 100% of the element -> avoid waiting to long if you have many elements */
  }
  to {
    transform: translateX(-100%);
  }
}

@media(max-width:998px) {
  .ticker {
    animation: marquee 20s linear infinite !important;
  }
  .ticker-item {
    margin-right: 10px !important;
  }
}
<div class="ticker-container">
  <div class="ticker" id="ticker">
    <!-- Ticker items will be inserted here dynamically -->
  </div>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way for me to retrieve the name and extension of an attached file in an input field of type "text"?

Could you assist me with a task? I have a table and I would like to extract the name and extension of each file and insert it into an input field below each "file" type input. This information will then be saved into a MySQL database, allowing me to create ...

"Enhance Your Online Shopping Experience with Woocommerce Ajax Filters and

I have a structure that looks like this: Company Google Apple Microsoft Material Wood Metal Plastic Essentially, Brand & Material are attributes in the Woocommerce system, while the rest are variables. I am currently working on implementing AJAX fi ...

JSHow to dynamically access child elements in objects

I am facing a challenge in dynamically accessing a JSObject item: jsonElement = { name:"name", subElement { subElementName: "nameSubElement", } } Currently, I have the element as: //level = "name" jsonElement[level] -> it works //lev ...

Is it possible for Ajax to actually make GET requests to the server, and if it does, why doesn't it appear in Firebug?

I have a LAMP setup on my Ubuntu and I am attempting to use Ajax to print the output in an unordered list. However, I am not seeing any results and there are no server calls showing up in Firebug. Here is the HTML file where the call is made: <!DOCTYP ...

What is the process for toggling the expansion and collapse of a table row (tr)?

Based on my previous experience (such as this example), I have observed that a div container can be easily toggled to hide and show. However, I am facing confusion when I have content inside a tr and I wish to display and hide certain items when that tr is ...

Incorporate interactive click buttons into your Telegram bot

Currently working on a telegram bot using node.js with the telegram-bot API by yagop. https://github.com/yagop/node-telegram-bot-api My goal is to allow users to stop typing messages and instead just click on buttons that will trigger actions. When a user ...

Using ThreeJS to project a texture onto a mesh surface

I am trying to project a texture onto the surface of a mesh using ThreeJS. The link provided achieves the desired result, although I am uncertain about the method used to accomplish it. . As I continue my research, I will update this post. If anyone has ...

The issue lies in the error code TS2315 which states that the type 'observable' is not a generic

I keep encountering an error message that says 'observable is not generic' while importing files. I am working on implementing CRUD operations in Angular 7, where I have created two components for adding and listing employees. The functions for c ...

Tips for setting up listeners across multiple modules using socket.io

Last year, I created a multiplayer game using node.js and socket.io. Now, as part of my efforts to enhance the game, I am working on breaking down the code into modules. Currently, I am utilizing expressjs 4.4 along with socket.io 1.0. One challenge I enco ...

Guide on showing a dropdown menu depending on the data in the current array index within a table

I am working with an object array that I want to display in a table. My goal is to have a dropdown select if the 'validvalues' field is not empty. How can I achieve this so that each row in the table has different options from the array? When &ap ...

What is the reason that in Node/Express, you are unable to pass the response object to a helper function for tasks like validation in order to prevent the ERR_HTTP_HEADERS_SENT error from

My web app is built using Node (v.18.2) and Express (v. 4.18). Users can make POST requests, which I validate upon arrival. If a user makes an error, I send back an error message to inform them of what went wrong. In order to streamline this process, I de ...

How to assign a property to an object within an array in VueJS?

Currently, I am creating a small list that includes car companies with an option for users to select their favorite one. Within this list, there is an array of objects containing the name and a property called starred. I have a method named setStarred whic ...

What steps can I take to eliminate the warning "Warning: Prop `style` did not match" that appears when I bring in the Image component in Next.js?

Hi there! I'm encountering a warning when trying to import an image using the Image component in nextjs. The warning message reads: Warning: Prop style did not match. Server: "display: block; max-width: 100%; width: initial; height: initial; ba ...

What's causing this element to overlap the previous one (apologies, once more)?

This is the code: <form id="the_form" action="">ev </div> <!-- final pay_block --> <div class="linea"/> <div class="spacer"/> The "linea" element is currently overlapping the buy button because a margin-bottom of 15px has be ...

javascript Href and onclick malfunctioning

I am encountering an issue with a form on my webpage: <form name="myForm" id="myForm" method="post" action="someUrl.php"> ... </form> There is a div outside of this form which contains an anchor link: <a href="anotherUrl.php" onclick="doc ...

Steps to create a submit button that is linked to a URL and includes an image

I'm attempting to convert my submit button into an image that, when clicked, redirects to another page. Currently, the submit button is functional but lacks the desired image and href functionality. <input type="submit" name="submit" alt="add" o ...

a tutorial on linking component data to a prop value

Is there a way to connect the searchString value in my Vue component to the item value in the html template it uses? I need to pass this value to the method called in my Ajax request. Vue: Vue.component('user-container-component', { props: ...

The error message states: `res.Send function is not recognized as a valid

Recently, I've been working on a function that goes like this: app.get('/counter', function (req, res) { console.log('/counter Request'); var counter = 0; fs.readFile(COUNTER_FILE_NAME, function(err, data) { c ...

Send two field values via axios by utilizing a b-form-select component from the Bootstrap Vue library

I'm struggling to send two fields to my backend, but every time I attempt to do so, both values end up as null in the backend. I am uncertain about what mistake I might be making. <template> <div id="app"> <div> ...

Is there a way to deactivate the Edge mini menu while selecting text in a React application?

I have recently been working on a React web application and managed to create a specialized text selection menu. However, I am facing a challenge in programmatically disabling the default text selection mini menu in React. The image attached illustrates bo ...