Ensure a button is automatically highlighted within an active class script

I have created a set of buttons that allow users to change the font family of the text. I would like the border and text color to update automatically when a specific option is selected.

Currently, the JavaScript code works well, but when the page first loads, the default font family (Georgia) is not highlighted until the user clicks on a button. How can I ensure that the default font style is already highlighted before any changes are made?

var fontbtns = document.getElementsByClassName("grid-item");
for (var i = 0; i < fontbtns.length; i++) {
  fontbtns[i].addEventListener("click", function() {
    var current = document.getElementsByClassName("fontactive");
    if (current.length > 0) {
      current[0].className = current[0].className.replace(" fontactive", "");
    }
    this.className += " fontactive";
  });
}
.grid-item.fontactive {
  border-color: #06a0ff;
  color: #06a0ff;
}
<div class="fontsgrid" id="fontlist">
  <button class="grid-item" id="ff-garamond" onclick="changeFontFamily('EB Garamond')">Garamond</button>
  <button class="grid-item" id="ff-palatino" onclick="changeFontFamily('Palatino')">Palatino</button>
  <button class="grid-item" id="ff-bookerly" onclick="changeFontFamily('Bookerly')">Bookerly</button>
  <button class="grid-item" id="ff-georgia" onclick="changeFontFamily('Georgia')">Georgia</button>
  <button class="grid-item" id="ff-baskerville" onclick="changeFontFamily('Baskerville')">Baskerville</button>
  <button class="grid-item" id="ff-montserrat" onclick="changeFontFamily('Montserrat')">Montserrat</button>
  <button class="grid-item" id="ff-futura" onclick="changeFontFamily('Futura')">Futura</button>
  <button class="grid-item" id="ff-opendyslexic3" onclick="changeFontFamily('OpenDyslexic3')">Open Dyslexic</button>
</div>

Answer №1

To incorporate the fontactive class into a specific button, simply add it to the desired button's class list.

var fontbtns = document.getElementsByClassName("grid-item");
for (var i = 0; i < fontbtns.length; i++) {
  fontbtns[i].addEventListener("click", function() {
    var current = document.getElementsByClassName("fontactive");
    if (current.length > 0) {
      current[0].className = current[0].className.replace(" fontactive", "");
    }
    this.className += " fontactive";
  });
}
.grid-item.fontactive {
  border-color: #06a0ff;
  color: #06a0ff;
}
<div class="fontsgrid" id="fontlist">
  <button class="grid-item" id="ff-garamond" onclick="changeFontFamily('EB Garamond')">Garamond</button>
  <button class="grid-item" id="ff-palatino" onclick="changeFontFamily('Palatino')">Palatino</button>
  <button class="grid-item" id="ff-bookerly" onclick="changeFontFamily('Bookerly')">Bookerly</button>
  <button class="grid-item fontactive" id="ff-georgia" onclick="changeFontFamily('Georgia')">Georgia</button>
  <button class="grid-item" id="ff-baskerville" onclick="changeFontFamily('Baskerville')">Baskerville</button>
  <button class="grid-item" id="ff-montserrat" onclick="changeFontFamily('Montserrat')">Montserrat</button>
  <button class="grid-item" id="ff-futura" onclick="changeFontFamily('Futura')">Futura</button>
  <button class="grid-item" id="ff-opendyslexic3" onclick="changeFontFamily('OpenDyslexic3')">Open Dyslexic</button>
</div>

It is recommended not to use the getElementsByClassName() method due to its impact on performance. Instead, opt for the querySelectorAll() method which returns a static node list.

Hence, with the adjustment, the code would look like:

// Utilize .querySelectorAll() as it simplifies looping
document.querySelectorAll(".grid-item").forEach(function(item){
  item.addEventListener("click", function() {
    document.querySelectorAll(".fontactive").forEach(function(element){
      element.classList.remove("fontactive");
    });
    item.classList.add("fontactive");
  });
});


function changeFontFamily(){}
.grid-item.fontactive {
  border-color: #06a0ff;
  color: #06a0ff;
}
<div class="fontsgrid" id="fontlist">
  <button class="grid-item" id="ff-garamond" onclick="changeFontFamily('EB Garamond')">Garamond</button>
  <button class="grid-item" id="ff-palatino" onclick="changeFontFamily('Palatino')">Palatino</button>
  <button class="grid-item" id="ff-bookerly" onclick="changeFontFamily('Bookerly')">Bookerly</button>
  <button class="grid-item fontactive" id="ff-georgia" onclick="changeFontFamily('Georgia')">Georgia</button>
  <button class="grid-item" id="ff-baskerville" onclick="changeFontFamily('Baskerville')">Baskerville</button>
  <button class="grid-item" id="ff-montserrat" onclick="changeFontFamily('Montserrat')">Montserrat</button>
  <button class="grid-item" id="ff-futura" onclick="changeFontFamily('Futura')">Futura</button>
  <button class="grid-item" id="ff-opendyslexic3" onclick="changeFontFamily('OpenDyslexic3')">Open Dyslexic</button>
</div>

An alternate approach involves using radio buttons to ensure only one selection at a time without necessitating loops in your code. By hiding and styling radio button labels as buttons, you achieve the desired functionality with minimal JavaScript involvement.

This technique introduces more HTML complexity but reduces reliance on JavaScript.

Refer to the inline comments below for details.

// Assign default font
document.body.style.fontFamily = "Consolas";

// Handle click events at parent level to set page font based on selected radio button within 'wrapper'
document.getElementById("wrapper").addEventListener("click", function(event){
  // Update page font to match value of clicked radio button 
  document.body.style.fontFamily = event.target.value;
});
/* Hide radio buttons */
input[type='radio'] { display:none; }

/* Default label style resembling buttons */
input[type='radio'] + label {
  display:inline-block;
  box-shadow:1px 1px grey;
  background-color:#e0e0e0;
  padding:5px;
  border-radius:3px;
  font-family:Arial, Helvetica, sans-serif;
  cursor:pointer;
  width: 80px;
  font-size:.6em;
  text-align:center;
}

/* Styling for checked labels */
input[type='radio']:checked + label {
  box-shadow:-1px -1px grey;
  background-color:#f78d32;
}
<div id="wrapper">
  <input type="radio" id="rad1" name="rad" value="Comic Sans MS">
  <label for="rad1">Comic Sans</label>

  <!-- Pre-selected choice using checked attribute -->
  <input type="radio" id="rad2" name="rad" value="Consolas" checked>
  <label for="rad2">Consolas</label>

  <input type="radio" id="rad3" name="rad" value="Calibri">
  <label for="rad3">Calibri</label>

  <input type="radio" id="rad4" name="rad" value="Georgia">
  <label for="rad4">Georgia</label>

  <input type="radio" id="rad5" name="rad" value="Arial">
  <label for="rad5">Arial</label>

  <input type="radio" id="rad6" name="rad" value="Montserrat">
  <label for="rad6">Montserrat</label>

  <input type="radio" id="rad7" name="rad" value="Baskerville Old Face">
  <label for="rad7">Baskerville</label>

  <input type="radio" id="rad8" name="rad" value="Harrington">
  <label for="rad8">Harrington</label>
</div>

<h1>Page Heading</h1>
<p>paragraph</p>
<p>paragraph</p>
<p>paragraph</p>
<p>paragraph</p>
<p>paragraph</p>
<p>paragraph</p>

Answer №2

To simplify this, utilize data attributes and relocate the event handler attachment to the script. By referencing the values of the data element, you can clearly see the fallback for a sans-serif font that has been added.

The removal of unnecessary IDs results in cleaner HTML structure.

function handleFontChangeClick(event) {
  const currentActive = document.querySelectorAll('.fontactive');
  currentActive.forEach(function(el) {
    el.classList.toggle("fontactive", false);
  });
  this.classList.toggle("fontactive", true);
}
// Attach a click handler to the nodes - in this case buttons
const fontbtns = document.querySelectorAll(".grid-item");
fontbtns.forEach(btn => btn.addEventListener('click', handleFontChangeClick));

// Direct way to set the class
// fontbtns[fontbtns.length - 1].classList.toggle("fontactive", true);

// A more efficient way to trigger "click" change?
// fontbtns[fontbtns.length - 1].click();

// Set a specific font from the list
const defaultFontFamily = "Georgia";
const fh = [...fontbtns].filter((el) => {
  return el.dataset.fontfam == defaultFontFamily;
}).pop().click(); // Automatically set on first one via click

// Set the fonts for the buttons based on their specified values
for (const btn of fontbtns) {
  btn.style.fontFamily = btn.dataset.fontfam;
}
.grid-item.fontactive {
  border-color: #06a0ff;
  color: #06a0ff;
}
<div class="fontsgrid" id="fontlist">
  <button class="grid-item" data-fontfam="EB Garamond">Garamond</button>
  <button class="grid-item" data-fontfam="Palatino">Palatino</button>
  <button class="grid-item" data-fontfam="Bookerly">Bookerly</button>
  <button class="grid-item" data-fontfam="Georgia">Georgia</button>
  <button class="grid-item" data-fontfam="Baskerville">Baskerville</button>
  <button class="grid-item" data-fontfam="Montserrat,sans-serif">Montserrat</button>
  <button class="grid-item" data-fontfam="Futura">Futura</button>
  <button class="grid-item" data-fontfam="OpenDyslexic3">Open Dyslexic</button>
</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

When I use the jQuery foreach loop, I notice that it produces identical results for both values

Hey there, I'm encountering a new issue with this jQuery script. In my foreach loop, I am extracting the product names and descriptions. The problem arises when I have 2 distinct products with different descriptions, but the following code: <d ...

Using Node.js to showcase MySQL data in an HTML table

Currently, I am in the process of learning how to utilize node.js with mysql. Despite my efforts to search for comprehensive documentation, I have not been successful. I have managed to display my mysql data on my browser, but ultimately I aim to manage it ...

Strategies for troubleshooting asynchronous JavaScript with multiple script loading

Typically, I am familiar with setting breakpoints, inspecting variables, and stepping into functions. The file Default.htm contains numerous scripts and empty placeholders. I prefer to proceed through debugging step-by-step. Unfortunately, setting a brea ...

Utilize JSON data to display markers on Leaflet maps

I am exploring the world of Leaflet and I have a question about loading markers from a database into a leaflet map using PHP. In my PHP code, I extract latitude and longitude data from the database based on the selected ward and encode it in JSON format. ...

CSS code that fixes a textarea at the bottom of a div

I need help placing a textarea at the bottom of a fixed div: <div id=msg> <div id=content> aaaaaaaannnnnnnnnn<br> aaaaaaaannnnnnnnnn<br> aaaaaaaannnnnnnnnn<br> </div> <div id=text> <textar ...

Generate nth-child selectors in a Material-UI component using props dynamically

I am currently working on customizing the Material UI slider component, specifically focusing on its marks prop to display the number of occurrences for each data object within the marks array. The desired appearance of the slider is illustrated in this i ...

Returning to the previous page is done by navigating back using the previous URL in Next.js

Recently, I encountered a situation where I had implemented filters on a webpage. Upon applying the filters, the URL of the page would change and additional query strings would be appended to the end. This caused an issue when navigating back using the b ...

Having trouble with uploading an image in Laravel using a modal?

For my laravel inventory system, I am facing an issue with uploading images for products. The old code I used for image upload is not working in my current project where I am utilizing a modal and ajax request to save inputs in the database. Can anyone pro ...

The 'background-size:cover' property does not function properly on iPad devices

Greetings! I have been working on developing a parallax website, and everything seems to be running smoothly across all browsers except for iPad. It appears that the background-size: cover property is not functioning properly on this device. Below is the ...

Using an array to dynamically input latitude and longitude into a weather API call in PHP

I am currently working on a leaflet map project that showcases the top 10 cities in a selected country. The markers are added dynamically based on the coordinates provided in the $latLng array. For example, when Australia is chosen from the select field, t ...

Error: selenium web driver java cannot locate tinyMCE

driver.switchTo().frame("tinymce_iframe"); String script="var editor=tinyMCE.get('tinymce_textarea');"; JavascriptExecutor js=(JavascriptExecutor) driver; js.executeScript(script); I'm encountering a WebDriverException while try ...

What are the solutions for resolving the TypeError when undefined is not an object error?

I'm currently working on a project where I need to fetch data from a JSON file using XMLHttpRequest and then store it in an array. However, I keep getting errors when attempting to do so. Take a look at this code snippet that seems to be causing the ...

Add the content script to a webpage just one time

I am looking to inject a content script on context menu click in an extension manifest version 3. I need a way to determine if the script is already injected or not. If it hasn't been injected yet, I want to inject the content script. This condition m ...

Enhancing CSS compatibility for Internet Explorer 9 with CSS3

I need to create a Jagged Edge border without relying on an image, and I want it to be compatible with IE9 and newer versions. Currently, it works in most browsers but not in IE9. Any suggestions or advice would be greatly appreciated. Thank you! Below is ...

Trouble displaying certain HTML code and its output on the browser?

I am in the process of developing a user profile page. I have designed a form with various fields, but the code within the section is not displaying correctly in the browser. I attempted to inspect the elements, but the code within that section remains inv ...

Unable to view sidebar navigation on the screen

I've been experimenting with the sidebar navigation from w3 schools, specifically trying to create a side-nav that opens from one div. You can see an example here: http://www.w3schools.com/w3css/tryit.aspfilename=tryw3css_sidenav_left_right&stack ...

Asynchronous data fetching with React Hook useEffect does not properly populate the tooltip in Material UI component

After using useEffect to fetch data, I encountered a problem in passing the data to my component. Here is some of my code: User Data Types (UPDATED) export interface IUser { display_name: string; id: string; images: Image[]; } expo ...

Embracing Interfaces Over 'any' Types in TypeScript

https://i.stack.imgur.com/W6NMa.pngWould it be beneficial to utilize an interface as a variable type rather than opting for any? For instance, if I have 3 functions where I am declaring variables that can contain alphanumeric data, would defining them us ...

How can I delete a hyperlink on a number from my iPhone or iPad?

Is there a way to prevent certain sets of numbers from being linked, like phone numbers do on iPhones? <h2>Table</h2> <table class="table table-sm table-striped table-hover"> <caption class="sr-only">Search Results</caption> ...

Connecting radio buttons to data in Vue using render/createElement

How do you connect the value of a selected radio button to a specific variable in the data object within a Vue application when using the render/createElement function? ...