Convert the easeInExpo function from jQuery easing to vanilla JavaScript and CSS

Currently, I am in the process of converting a piece of code from jQuery to plain JavaScript and CSS. The specific code snippet I am focusing on involves creating easing functions without relying on jQuery.

const customEasing = {
    easeInExpo: function (x, t, b, c, d) {
        return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
    }
};

let nset = false;

document.querySelector('button#hmenu').addEventListener('click', function() {
    if (!nset) {
        document.querySelector('ul#nav').style.transitionDelay = '35ms';
        document.querySelector('ul#nav').style.transitionDuration = '300ms';
        document.querySelector('ul#nav').style.transitionTimingFunction = customEasing.easeInExpo;
        
        document.querySelector('ul#nav').classList.add('active');
        nset = true;
    } else {
        document.querySelector('ul#nav').style.transitionDuration = '550ms';
        document.querySelector('ul#nav').classList.remove('active');
        nset = false;
    }
});

As I explore CSS transitions with easing functions, I'm curious about the available options. In my existing code, there are slideDown and slideUp easing functions that are utilized for a mobile menu navigation in a production environment.

Answer №1

Latest Update:

Check out this GitHub repository called "You don't need jQuery." It contains a comprehensive list of common jQuery functions rewritten in vanilla JavaScript.

You'll find animations, query selectors, Ajax, events, and other advanced jQuery features all covered here.

For those looking to support older IE versions, the slightly older resource youmightnotn eedjquery.com is also very helpful.

If you want an animation with an `easeInExpo` style, just use this CSS code snippet provided by this website:

transition: all 500ms cubic-bezier(0.950, 0.050, 0.795, 0.035);

The example below demonstrates this easing property on the `height` of a div. I've adjusted it to include the delay added in jQuery when clicked (35ms), timings (300ms and 550ms respectively), and jQuery's default easing ('swing') - courtesy of this answer for reference:

let expandable = document.getElementById('expandable');
let expandButton = document.getElementById('expand-button');

expandButton.addEventListener('click', () => {
  expandable.classList.toggle('expanded');
});
#expandable {
  background: red;
  transition: all 550ms cubic-bezier(.02, .01, .47, 1);
  height: 0px;
  width: 100px;
  transition-delay: 0ms;
}

#expandable.expanded {
  height: 100px;
  transition-delay: 35ms;
  transition: all 300ms cubic-bezier(0.950, 0.050, 0.795, 0.035);
}
<div id="expandable"></div>
<br />
<button id="expand-button">Toggle expand</button>

Answer №2

To make your collapsible component expand to a variable height, some magic is required. Instead of expanding to a fixed height every time, you can create a class like "expanded" and toggle it on and off to control the height dynamically.

For variable heights, you'll need to use JavaScript to determine the height of each expandable element and adjust those values if the window size changes due to text wrapping or image resizing. Custom CSS properties with fallback values to unset can achieve this functionality smoothly.

The example below showcases both a toggle and accordion scenario, utilizing a cubic bezier easing function similar to easeInExpo. The easing function

cubic-bezier(0.95, 0.05, 0.795, 0.035)
was sourced from easings.net, which offers pure CSS easings for various effects.

Simple Demo

// JavaScript logic for expanding elements
const expandables = document.querySelectorAll('.expandable');
const setInnerHeights = () => {
  for (const expandable of expandables) {
    expandable.style.setProperty('--inner-height', Array.from(expandable.children).map(child => child.offsetHeight).reduce((a, c) => a + c, 0) + 'px');
  }
};
setInnerHeights();
document.addEventListener('click', e => {
  if (e.target?.matches('.expand-trigger')) {
    const expandable = e.target.nextElementSibling;
    expandable.classList[expandable.classList.contains('expanded') ? 'remove' : 'add']('expanded');
  }
});
window.addEventListener('resize', setInnerHeights);
/* CSS styles for expanding elements */
html {
  height: 100%;
  box-sizing: border-box;
}
*, *::before, *::after {
  box-sizing: inherit;
}
body {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  min-height: 100%;
  padding: 20px;
}
.expandable {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s cubic-bezier(0.95, 0.05, 0.795, 0.035);
  text-align: left;
}
.expandable > p {
  margin: 0;
  padding: 10px 0;
}
.expandable.expanded {
  --content-height: calc(var(--inner-height) + 20px);
  max-height: var(--content-height, unset);
}
<button class="expand-trigger">Expand #1</button>
<div class="expandable">
  <p>Lorem ipsum dolor sit amet.</p>
</div>
<button class="expand-trigger">Expand #2</button>
<div class="expandable">
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
</div>
<button class="expand-trigger">Expand #3</button>
<div class="expandable">
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit...</p>
  <p>Integer convallis lectus eu felis bibendum, vel lacinia metus imperdiet...</p>
</div>

Advanced Demo

// JavaScript logic for accordion functionality
const initAccordions = () => {
  const getNode = selector => document.querySelector(selector),
      getNodes = selector => Array.from(document.querySelectorAll(selector)),
      findChildren = (node, selector) => Array.from(node.children).filter(e => e.matches?.(selector)),
      findChild = (node, selector) => Array.from(node.children).find(e => e.matches?.(selector)),
      _addInput = (node, position, id, checked) => node.insertAdjacentHTML(position, `<input type="radio" name="accordion-${id}"${checked ? ' checked="checked"' : ''}>`),
      setInnerHeight = node => {
        const height = Array.from(node.children).map(child => child.offsetHeight).reduce((a, c) => a + c, 0) + 'px';
        node.style.setProperty('--inner-height', height);
      },
      accordions = Array.from(document.querySelectorAll('.accordion'));
  let accordionIndex = 0;
  for (const accordion of accordions) {
    const isToggle = accordion.dataset?.type === 'toggle',
        panels = findChildren(accordion, '.accordion--panel');
    let panelIndex = 0;
    for (const panel of panels) {
      const title = findChild(panel, '.accordion--panel--title'),
          content = findChild(panel, '.accordion--panel--content'),
          addInput = (node, position, checked) => _addInput(node, position, accordionIndex + (isToggle ? '-' + panelIndex : ''), checked);
      setInnerHeight(content);
      addInput(title, 'beforebegin');
      addInput(title, 'afterbegin', true);
      panelIndex++;
    }
    accordionIndex++;
  }
  window.addEventListener('resize', () => {
    const panelContents = Array.from(document.querySelectorAll('.accordion > .accordion--panel > .accordion--panel--content'));
    for (const content of panelContents) setInnerHeight(content);
  });
};
initAccordions();
/* Additional CSS styles for accordions */
html {
  height: 100%;
  box-sizing: border-box;
}
*, *::before, *::after {
  box-sizing: inherit;
}
body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  min-height: 100%;
  padding: 20px;
}
 /* Custom styling for accordion components */
...
Accordion and Toggle Demos...

You can explore more CSS easing functions at easings.net. Additionally, Chrome's DevTools allow you to visually customize easing functions by adjusting the transition-timing-function property within your stylesheets.

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

The Typescript const assertion translated into Javascript

Is there a way in JavaScript to achieve constant-like behavior similar to TypeScript's const assertion? const arr = [1,2,3,4] as const I am looking for a solution in JavaScript that allows me to create an array that cannot be further mutated. ...

Introducing additional choices to the list and automatically refreshing the list with the latest updates

I am currently honing my skills in Yii2 by working on a project using this framework. One of the challenges I am facing is adding new list options dynamically without having to navigate away from the current page. When I click the "Add new option" button ...

Are there ways to incorporate extra icons into NavMenu in Blazor 8?

I am exploring ways to incorporate extra icons into the NavMenu.razor component of my Blazor version 8 application. In the earlier version, Blazor 7, there was an iconset setup located in wwwroot/css/, which allowed me to include additional icons simply by ...

Function invoking React Hook

I'm a beginner to React JS and I've been learning by working on a project. I was creating a basic e-commerce UI with items and attempting to add items to a cart, but encountered an issue. The React Hook "useStateValue" is being called in the fun ...

Guide to dividing a URL in reactjs/nextjs

Here is the complete URL: /search-results?query=home+floor&categories=All+Categories. I am looking to separate it into two sections - /search-results and query=home+floor&categories=All+Categories. My objective is to extract the second part of t ...

Updating and eliminating text within an array of objects using Vue JS

My Axios request pulls in an array of objects named 'uniquecolors'. Here is what it looks like: mycolors color: [GREEN, RED, BLUE, YELLOW, ORANGE,ORANGE,GREEN,] color: [GREEN, RED, BLUE, YELLOW, ORANGE,ORANGE,GREEN,] color ...

What is the best way to transition an absolute positioned element from right to center?

When hovering over an overlay element, I want the <h3> tag to appear with a transition effect from right to center, similar to the example shown here. Could someone please assist me in achieving this? Thank you in advance. HTML <div class="row m ...

The issue of duplicate results appearing in the Wikipedia viewer persists even after conducting a second search

I have been working on a project to create a wiki viewer, but I've encountered an issue. Currently, I am utilizing the Wikipedia API. When a user enters a search query, they are presented with 5 possible articles (title and first sentence), and upon ...

Navigating with VueRouter in your Chrome Extension is a breeze

I have been working on a Chrome extension using Vue 3 + VueRouter. One issue I encountered was trying to change the router-view content to display a different component, essentially showing users a different UI. Despite my efforts and various methods use ...

Sending Data from Browser to Node.js using Ajax

I've been struggling to send an AJAX post request to my Node server built with Express for a while now. Despite reading various solutions on similar issues, I couldn't figure out which files to edit. Initially, I attempted using `http.createServe ...

What methods can I utilize to transmit Global variable data from a view to a controller?

In my Angular view file, I have the following code snippet. <!DOCTYPE html> <video id="myVideo" class="video-js vjs-default-skin"></video> <script> var dataUri; var videoData; var player = videojs("myVideo", { controls ...

Steps for interacting with a button of the <input> tag in Selenium using Python

As I attempt to complete a form submission, I encounter an issue where clicking the submit button does not produce any action. It seems that the problem lies with the button being tagged as <input>: <input type="submit" name="submit ...

I keep encountering an error that says "ReferenceError: localStorage is not defined" even though I have already included the "use

I have a unique app/components/organisms/Cookies.tsx modal window component that I integrate into my app/page.tsx. Despite including the 'use client' directive at the beginning of the component, I consistently encounter this error: ReferenceErr ...

unable to successfully npm install canvas

For my GitHub repository, please visit here This project was actively developed until November of last year, after which I did not commit any changes. Today, I attempted to run the project again but encountered the following error. My current system versi ...

The dropdown menu is obscured by the toolbar

When using ionic4/angular, I want a dropdown menu to display upon clicking on the ion-avatar. However, the dropdown menu is hiding in the toolbar. I have tried setting z-index but it did not work. Any suggestions would be appreciated. You can find the sta ...

If the session cannot be located, users will be redirected to the sign-in page

I have a small application that utilizes next-auth to display a signin/signout button based on the user's sign-in status. The buttons function correctly and redirect me to the signin page when clicked. However, I am wondering how can I automatically ...

New Relic identifies mysterious delays caused by MongoDB's findOne method

After setting up newrelic to pinpoint the bottlenecks in my app, I discovered a major issue that has left me stumped. The source of most delays seems to be mongoDB user.findOne, but the biggest challenge is locating where in the code this delay is occurri ...

An error occurred when attempting to hide or show a jQuery loading animation

Here is the HTML code I am using: <div id="success_message" style="display:none"> <p>Good job!</p> </div> <div id="my-form"> <form> <input>....... (lots of inputs) <input id="my-btn" ...

A guide on embedding the flag status within the image tag

I would like to determine the status of the image within the img tag using a flag called "imagestatus" in the provided code: echo '<a href="#" class="swap-menu"><img id="menu_image" src="images/collapsed.gif" hspace = "2"/>'.$B-> ...

Discover the perfect method for combining two objects while updating any empty values with a new specified value. Furthermore, in the case where the new value is also

My task involves working with an array of objects where each time I select a value, it gets pushed into the array. My goal is to merge two objects that share the same key "code" and remove any empty values. (4) [{…}, {…}, {…}, {…}] 0: {code: "abc ...