I've written a simple JS that takes care to animate an icon.
At first I wrote it quickly and without paying attention to any kind of optimization:
import '../js/core/sidenav.js';
const SIDENAV_ITEM_USER = document.getElementById('sidenav-item-user');
const SIDENAV_ITEM_SEARCH = document.getElementById('sidenav-item-search');
const SIDENAV_ITEM_ORDER = document.getElementById('sidenav-item-order');
const SIDENAV_ITEM_FILTER = document.getElementById('sidenav-item-filter');
let sidenav_item_icon_angle_user = '0';
let sidenav_item_icon_angle_search = '0';
let sidenav_item_icon_angle_order = '0';
let sidenav_item_icon_angle_filter = '0';
SIDENAV_ITEM_USER.addEventListener('click', function() {
rotate('user');
});
SIDENAV_ITEM_SEARCH.addEventListener('click', function() {
rotate('search');
});
SIDENAV_ITEM_ORDER.addEventListener('click', function() {
rotate('order');
});
SIDENAV_ITEM_FILTER.addEventListener('click', function() {
rotate('filter');
});
function rotate(target) {
if (target == 'user') {
var element = document.getElementById('sidenav-item-icon-user');
if (sidenav_item_icon_angle_user == 0) {
sidenav_item_icon_angle_user = '180';
} else {
sidenav_item_icon_angle_user = '0';
}
element.style.transform = 'rotate(' + sidenav_item_icon_angle_user + 'deg)';
}
if (target == 'search') {
var element = document.getElementById('sidenav-item-icon-search');
if (sidenav_item_icon_angle_search == 0) {
sidenav_item_icon_angle_search = '180';
} else {
sidenav_item_icon_angle_search = '0';
}
element.style.transform = 'rotate(' + sidenav_item_icon_angle_search + 'deg)';
}
if (target == 'order') {
var element = document.getElementById('sidenav-item-icon-order');
if (sidenav_item_icon_angle_order == 0) {
sidenav_item_icon_angle_order = '180';
} else {
sidenav_item_icon_angle_order = '0';
}
element.style.transform = 'rotate(' + sidenav_item_icon_angle_order + 'deg)';
}
if (target == 'filter') {
var element = document.getElementById('sidenav-item-icon-filter');
if (sidenav_item_icon_angle_filter == 0) {
sidenav_item_icon_angle_filter = '180';
} else {
sidenav_item_icon_angle_filter = '0';
}
element.style.transform = 'rotate(' + sidenav_item_icon_angle_filter + 'deg)';
}
}
Subsequently, having verified that the animations work, I have optimized the code:
import '../js/core/sidenav.js';
const SIDENAV_CONTENT = document.getElementById('sidenav-content');
const SIDENAV_CONTENT_NODES = SIDENAV_CONTENT.querySelectorAll('a');
var angles = new Map();
SIDENAV_CONTENT_NODES.forEach(function(target) {
if (target.getAttribute('id').includes('item')) {
var identifier = target.getAttribute('href').split('-')[3];
angles.set(identifier, '0');
target.addEventListener('click', function() {
rotate(identifier);
});
}
});
function rotate(target) {
var element = document.getElementById(`sidenav-item-icon-${target}`);
angles.get(target) == '0' ? angles.set(target, '180') : angles.set(target, '0');
element.style.transform = `rotate(${angles.get(target)}deg)`;
}
To better understand, this is my HTML:
<div id="sidenav-content" class="sidenav-content">
<div id="sidenav-general-mobile-view">
<ul>
<li>
<a id="nav-icon" href="#"><img class="nav-icon" src="../assets/images/nav_cart.svg" height="25">Carrello<span class="badge rounded-pill bg-danger nav-badge">5</span></a>
</li>
<li>
<div class="card card-user">
<div class="card-header">
<a data-bs-toggle="collapse" id="sidenav-item-user" href="#sidenav-item-body-user" role="button" aria-expanded="false" aria-controls="sidenav-item-body-user">
<img class="icon" src="../assets/images/nav_user.svg" height="25">
<p class="d-inline mb-0">Accedi</p>
<img class="arrow" src="../assets/images/sn_general_down.svg" id="sidenav-item-icon-user" height="14" alt="sidenav_item_user_icon">
</a>
</div>
<div class="card-body collapse" id="sidenav-item-body-user">
<div class="p-16">
<ul class="mb-0">
<li>
<a id="nav-signup" href="#">Registrati</a>
</li>
<li>
<a id="nav-signin" href="#">Accedi</a>
</li>
</ul>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" id="sidenav-item-search" href="#sidenav-item-body-search" role="button" aria-expanded="false" aria-controls="sidenav-item-body-search">
<p class="d-inline mb-0">Cerca</p>
<img class="arrow" src="../assets/images/sn_general_down.svg" id="sidenav-item-icon-search" height="14" alt="sidenav_item_search_icon">
</a>
</div>
<div class="card-body collapse" id="sidenav-item-body-search">
<div class="p-16">
<div class="input-group input-search">
<span class="input-group-text">
<img src="../assets/images/input_search.svg" height="14" alt="input-search-icon">
</span>
<input type="text" class="form-control" placeholder="Cerca nel catalogo" aria-label="input-search" aria-describedby="basic-addon1">
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" id="sidenav-item-order" href="#sidenav-item-body-order" role="button" aria-expanded="false" aria-controls="sidenav-item-body-order">
<p class="d-inline mb-0">Ordina</p>
<img class="arrow" src="../assets/images/sn_general_down.svg" id="sidenav-item-icon-order" height="14" alt="sidenav_item_order_icon">
</a>
</div>
<div class="card-body collapse" id="sidenav-item-body-order">
<div class="p-16">
<div class="form-check">
<input class="form-check-input" type="radio" name="flexRadioDefault" id="order-default" checked>
<label class="form-check-label" for="order-default">Ultimi arrivi</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="flexRadioDefault" id="order-2">
<label class="form-check-label" for="order-2">Prezzo crescente</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="flexRadioDefault" id="order-3">
<label class="form-check-label" for="order-3">Prezzo decrescente</label>
</div>
<div class="form-check mb-0">
<input class="form-check-input" type="radio" name="flexRadioDefault" id="order-4">
<label class="form-check-label" for="order-4">In promozione</label>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" id="sidenav-item-filter" href="#sidenav-item-body-filter" role="button" aria-expanded="false" aria-controls="sidenav-item-body-filter">
<p class="d-inline mb-0">Filtra</p>
<img class="arrow" src="../assets/images/sn_general_down.svg" id="sidenav-item-icon-filter" height="14" alt="sidenav_item_filter_icon">
</a>
</div>
<div class="card-body collapse" id="sidenav-item-body-filter">
<div class="p-16">
</div>
</div>
</div>
</div>
And a video on how it works:
I would like to know if the "optimization" is necessary given the simplicity and length of the code.
Making my considerations:
Pro: A more streamlined and clean code and the ability to add new elements without having to touch the JS again. Against: Possible increased use of resources due to the use of objects and loops. (?)
Possibly do you know better ways to optimize this code?

[aria-expanded="true"] .arrow { transform: rotate(180deg) }. \$\endgroup\$