Skip to Content

Simple JavaScript

For lightweight client-side interactions — toggles, form enhancements, mobile menus, simple event listeners — vanilla JavaScript is often sufficient and easier to maintain than a full React setup.

Vanilla JavaScript files are configured as webpack entry points and built into bundles the same way as React assets, but they don’t require the React runtime library. Use React when you truly need component reusability, complex state, or data-driven interfaces.

File Organization

Create a dedicated folder for each feature under ui/components/semantic-ui/js/:

            • index.js
            • index.js
            • index.js
          • app.js

Common Patterns

DOM Ready

Wrap your code to ensure the DOM is fully loaded:

// Option 1: IIFE pattern (recommended) (function() { const element = document.getElementById('my-element'); // Your code here })(); // Option 2: DOMContentLoaded event document.addEventListener('DOMContentLoaded', () => { const element = document.getElementById('my-element'); // Your code here });

Event Delegation

For elements that may be dynamically added or removed, use delegation on a parent element:

document.addEventListener('click', (e) => { // Handle clicks on any element with the accordion-trigger class if (e.target.matches('.accordion-trigger')) { const content = e.target.nextElementSibling; content.classList.toggle('open'); } });

Search Input with Enter Key

document.addEventListener('DOMContentLoaded', () => { const searchInput = document.getElementById('home-search'); if (searchInput) { searchInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { const query = encodeURIComponent(searchInput.value.trim()); window.location.href = `/search?q=${query}`; } }); } });

Mobile Menu Toggle

(function() { const toggle = document.querySelector('.mobile-menu-toggle'); const menu = document.querySelector('.main-navigation'); if (toggle && menu) { toggle.addEventListener('click', () => { menu.classList.toggle('visible'); toggle.classList.toggle('active'); }); // Close menu when clicking outside document.addEventListener('click', (e) => { if (!menu.contains(e.target) && !toggle.contains(e.target)) { menu.classList.remove('visible'); toggle.classList.remove('active'); } }); } })();

Form Validation Hint

(function() { const inputs = document.querySelectorAll('input[data-validate]'); inputs.forEach(input => { const validateType = input.dataset.validate; input.addEventListener('blur', () => { const hint = document.querySelector(`#${input.id}-hint`); if (hint) { if (validateType === 'email' && !input.value.includes('@')) { hint.textContent = 'Please enter a valid email address'; hint.classList.add('error'); } else { hint.textContent = ''; hint.classList.remove('error'); } } }); }); })();

Scroll-Triggered Animations

(function() { const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animate-in'); observer.unobserve(entry.target); // Only animate once } }); }, observerOptions); document.querySelectorAll('[data-animate]').forEach(el => { observer.observe(el); }); })();

Date Input Enhancement

(function() { const dateInput = document.querySelector('input[type="date"]'); const today = new Date().toISOString().split('T')[0]; if (dateInput) { // Set min date to today dateInput.setAttribute('min', today); // Auto-format on manual entry dateInput.addEventListener('change', () => { const value = dateInput.value; if (value && !value.match(/^\d{4}-\d{2}-\d{2}$/)) { // Show hint for invalid format const hint = document.querySelector(`#${dateInput.id}-hint`); if (hint) { hint.textContent = 'Please use YYYY-MM-DD format'; } } }); } })();

jQuery Interoperability

InvenioRDM includes jQuery by default. You can use it if you prefer:

import $ from 'jquery'; $(document).ready(() => { $('.my-toggle-button').click(function() { $('.my-content').slideToggle(); }); // Mobile menu $('.mobile-menu-toggle').click(function() { $('.main-navigation').toggleClass('visible'); }); // AJAX form submission $('form[data-ajax="true"]').submit(function(e) { e.preventDefault(); const formData = $(this).serialize(); $.post($(this).attr('action'), formData) .done(() => { alert('Saved!'); }) .fail(() => { alert('Error saving'); }); }); });

Debugging

  • Use console.log() or console.debug() to output values
  • Add debugger; statement to pause execution when DevTools are open
  • Check the browser console for errors
  • Verify your element selectors exist by logging document.querySelector('.my-selector')

Further Reading

Last updated on