I am currently working on a larger project at the moment that requires a lot of front-end wrangling. As a part of this project, I needed to create Button Items that are generated by the user from both an HTML select element for one section and, a comma-separated text input in another section.
When a user selects an item from a select menu, a button appears in a desired area with the name of the selection and a small “X” that can be clicked to remove the item. Likewise, if a user wishes to create a bunch of items separated by commas in an input element and either hit the “Enter” or the “Add” button, then those items will be transformed as a bunch of individual buttons that the user can remove and change.
The buttons essentially become the user’s selection of items.
I also needed to be able to get a list of those item buttons for when I submit a form server-side or for some other task.
As a result, I created a small library called itemButton().
Note: If you have a bit of experience with front-end, then all you may need is to grab the CSS code and input.js file. If you need some further explanation you can find the information below.
What it does
The itemButton()
library:
- Creates a button named by the selection or the user’s input, This is removable should the user wish to change their choices.
- Allows you to select the max number of items you want your users to be able to choose.
- Easily extracts a list of buttons by ID and value.
An example
Take a look at an example:
My inability to consistently fail to place an ‘e’ at the end of giraffe notwithstanding, you can see that the user can select items from a select element and they will be displayed in a chosen area below. Further, when the user types some words and separates them by a comma they are displayed as buttons in a div below it.
Limit the number of displayed Item Buttons
You might have also noted that the select element will only display two-item buttons, while the comma-separated list will display up to 10. You are able to create a maximum limit of any of your text input or select elements you are running itemButton()
on.
Validation
When the user submits their items, itemButton()
will:
- Check for duplicates. If it already exists, it won’t be displayed again.
- Remove any empty comma-separated elements.
- Remove any non-alphanumeric characters.
- Cut any text input items between commas to less than or equal to 25 characters (You can change this if you want).
- Exchange any spaces between words with a dash for the button ID.
- Any item starting with a number gets and “a” at the start of the button ID so it can be used in the HTML.
Getting a list if item buttons
As you can see in the video above, when the user clicks the Log Items button, an object of ids and values for each item is listed in the browser console using the list()
method in itemButton()
. This is most useful for when you are submitting data to the server.
Let’s have a gander at the code:
The Code
I have chosen to put the Javascript itemButton()
library in an input.js file. In my project, I have other small classes or libraries in that file too. It is up to you how you want to add the code.
The CSS for the buttons is separate, and I have added it to my universal style.css file. Again you can put it anywhere you think works for you.
input.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
export { itemButton }; /** * Add, remove and list buttons from a coma separated text input or * from a select box. */ function itemButton() { let publicAPI = {}; /** * Adds a deletable button from either an input or select tag. * @param {string} items - single item or string of comma separated items. * @param {string} selectionsLoc - id of the location the item buttons will be placed. * @param {number} numSelections - Total number of items permitted. */ publicAPI.add = (items, selectionsLoc, numSelections) => { //Get the current list of button items. let currentItems = publicAPI.list(selectionsLoc); // If no items exists in button location item counter equals numSelection // else subtract the length of he current items. let itemCounter = !currentItems ? numSelections : numSelections - currentItems.length; if (itemCounter < 1) return; // If zero then max number of items has been met. // ****Validate***** // separate by coma (if it exists) then filter our blank items. items .split(",") .filter((item) => item.trim().length != 0) .forEach((item) => { if (itemCounter < 1) return; //Reduce string length to 30 characters. let itemShort = item.substring(0, 25); // ****Create ID and Values***** // Create ID let itemID = itemShort .trim() .replace(/\s+/g, "-") //Replace spaces with dashes. .replace(/[^0-9a-z-]/gi, "") //Remove anything that isn't alphanumeric. .toLowerCase(); //If itemID starts with a number add teh letter "a" so it is a valid HTML ID. itemID = isNaN(itemID[0]) ? itemID : `a${itemID}`; //Create Item Value let itemVal = itemShort.replace(/[^0-9a-z ]/gi, ""); //Remove anything that isn't alphanumeric. //Check if ID exits in current lists. let alreadyExists = false; if (currentItems) { for (let i = 0; i < currentItems.length; i++) { if (currentItems[i].id === itemID) { return (alreadyExists = false); } } } if (alreadyExists) return; // ****Create button***** //Create new span let selectionSpan = document.createElement("span"); selectionSpan.setAttribute("value", itemVal); selectionSpan.setAttribute("id", itemID); selectionSpan.setAttribute("class", "item-btn"); selectionSpan.innerHTML = `${itemVal} <button type="button" id="${itemID}" class="item-x-btn">✖</button>`; let selectLoc = document.querySelector(`#${selectionsLoc}`); selectLoc.appendChild(selectionSpan); // ****Add event listener for new button attached to item.**** let selectedSpan = selectLoc.querySelector(`#${itemID}`); //Add an event listener on the "x" button to remove the item if clicked. selectedSpan.querySelector("button").addEventListener("click", () => { selectedSpan.remove(); }); --itemCounter; currentItems = publicAPI.list(selectionsLoc); }); }; /** * Get all the items in a selected tag location or false if none exist. * @param {string} selectionsLoc id of the location the item buttons will be placed. * @return {object|boolean} object of all current item buttons in selected tag * containing {id: , val: }. If none then false. */ publicAPI.list = (selectionsLoc) => { let items = document.getElementById(selectionsLoc).querySelectorAll("span"); //If no items exists return false if (items.length == 0) { return false; } return Object.keys(items).map((item) => { return { id: items[item].id, val: (() => { let val = items[item].innerText; return val.substr(0, val.length - 2); })(), }; }); }; return publicAPI; } |
stlye.css
Add this to your main CSS file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/* Buttons style for item selections such as categories and tags. */ .item-btn { background-color: #cfcdcd; color: rgb(24, 24, 24); border-radius: 5px; border: none; padding: 2px; text-align: center; display: inline-block; margin: 2px; } .item-x-btn { display: inline-block; border: none; background-color: #cfcdcd; box-shadow: none; margin-left: 1px; border-radius: 50%; color: rgb(24, 24, 24); font-size: small; line-height: 1.28; transition: 0.3s; } .item-x-btn:hover { background-color: firebrick; color: whitesmoke; cursor: pointer; } .item-x-btn:focus { outline: none; } |
Quick use guide
Create the select button or input button
First, you will need to create your select or text input elements. Make sure that they have an appropriate ID that we can reference later. You can have as many select or text input elements as you want to reference the itemButton()
class.
Take a look at the example below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Example</title> <link rel="stylesheet" type="text/css" href="main.css" media="screen" /> </head> <body> <div class="container"> <h1 align="center">Create removable button item lists from text input and select tags</h1> <!-- Selection menu used to create item buttons --> <div> <div> <label for="items">Add your selection</label> <select id="items" name="items"></select> </div> </div> <!-- Comma separated list that creates item buttons. --> <div> <div> <label for="tags" >Add your tags <em>(separate by comma): </em></label ></label> <input type="text" name="tags" id="tags" /> </div> <div> <button id="addTag" type="button">Add</button> </div> </div> </form> </div> |
Here we have added a select element with an id of “items”. We also have a text input with an ID of “tags”. The “tags” element also has an “addTag” button that the user can use to add their tags.
Create a div or span for your item buttons to go
Next, we need to create a location to display your item buttons. In the example below, I have used a div directly below the select or input elements.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!-- Selection menu used to create item buttons --> <div> <div> <label for="items">Add your selection</label> <select id="items" name="items"></select> </div> <!-- Buttons for items selected appear here --> <div id="items-selections"></div> </div> <!-- Comma separated list that creates item buttons. --> <div> <div> <label for="tags" >Add your tags <em>(separate by comma): </em></label ></label> <input type="text" name="tags" id="tags" /> </div> <div> <button id="addTag" type="button">Add</button> </div> <!-- Buttons for tag appear here. --> <div id="tags-selection"></div> |
Importing the itemButton() code
Our next step will be inside the script tags of our HTML file. If you are importing just the input.js file into your HTML file you will need to invoke you script tags as a module :
1 2 3 |
<script type="module" src="input.js"> ... </script> |
If you are importing more than one file, like I am in the example, I recommend you import your Javascript files like this:
1 2 3 4 5 |
<script type="module"> import { items } from "./resources/items.js"; import { itemButton } from "./resources/input.js"; ... </script> |
In the example above, my two files are in the resources folder. The items.js file is just the file I have stored my list of select items.
Add event listeners
Your next task is to add event listeners for each of your elements. For me, I will add an “input” event for my select element.
For my text input, I will add both a “keypress” (Enter) listener if the user hits enter after typing in their items in the “tags” text input or a “click” if they hit the “Add” button. We will also clear out the tags after each use.
Take a look at the example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/** * Add a item button for items. */ itemsLoc.addEventListener("input", () => { ... }); /** * Add an item button for tags. */ document.querySelector("#addTag").addEventListener("click", () => { let val = document.querySelector("#tags").value; ... document.querySelector("#tags").value = ""; }); document.querySelector("#tags").addEventListener("keypress", (e) => { if (e.key === "Enter") { let val = document.querySelector("#tags").value; ... document.querySelector("#tags").value = ""; } }); |
itemButton()
itemButton().add()
Now it’s time to add the button. We do this inside each event listener.
The add()
method takes three arguments:
items
– A string containing a single item or comma-separated list of items.selectionsLoc
– This is a string containing the ID reference of the location you want to display your buttons, usually in a div or span.- >
numSelections
– The maximum total number of items you wish to have the user select.
itemButton().add(items, selectionsLoc, numSelections)
Back to our example for our select element, our item is the value of the current selection. The location that we want to display our item buttons is theitems-selection
div and the maximum number of items that our user can add is 2.
1 2 3 4 5 6 |
/** * Add a item button for items. */ itemsLoc.addEventListener("input", () => { itemButton().add(itemsLoc.value, "items-selections", 2); }); |
For our text input, the value is the comma-separated string of values that the user enters. The location the button items will be displayed in will be the tags-selection
div and we will allow the user to add up to 10 items.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * Add an item button for tags. */ document.querySelector("#addTag").addEventListener("click", () => { let val = document.querySelector("#tags").value; itemButton().add(val, "tags-selection", 10); document.querySelector("#tags").value = ""; }); document.querySelector("#tags").addEventListener("keypress", (e) => { if (e.key === "Enter") { let val = document.querySelector("#tags").value; itemButton().add(val, "tags-selection", 10); document.querySelector("#tags").value = ""; } }); |
itemButton().list()
To get a list of item buttons from any of your assigned areas that display them, you can use the list()
method.
This method takes one argument, the element id that the item buttons are contained in. The list method will return an object of key-value pairs containing:
1 2 3 4 |
{ id: element id, val: the displayed value of the button } |
The example
This example file setup is as follow:
- index.html
- main.css
- resources
- inputs.js
- items.js
Here is the sample HTML file below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Example</title> <link rel="stylesheet" type="text/css" href="main.css" media="screen" /> </head> <body> <div class="container"> <h1 align="center">Create removable button item lists from text input and select tags</h1> <!-- Selection menu used to create item buttons --> <div> <div> <label for="items">Add your selection</label> <select id="items" name="items"></select> </div> <!-- Buttons for items selected appear here --> <div id="items-selections"></div> </div> <!-- Comma separated list that creates item buttons. --> <div> <div> <label for="tags" >Add your tags <em>(separate by comma): </em></label ></label> <input type="text" name="tags" id="tags" /> </div> <div> <button id="addTag" type="button">Add</button> </div> <!-- Buttons for tag appear here. --> <div id="tags-selection"></div> </div> <hr> <button id="submit" type="button">Log Items</button> </form> </div> <script type="module"> //Bring in list of items file and the item button methods. import { items } from "./resources/items.js"; import { itemButton } from "./resources/input.js"; /** * Generate options for item selection menu. */ let options = `<option id="items-option1" disabled selected value>-- select an option --</option>`.concat( items .map((item) => { return `<option value="${item}">${item}</option>`; }) .join("\n") ); let itemsLoc = document.querySelector("#items"); itemsLoc.innerHTML = options; /** * Add a item button for items. */ itemsLoc.addEventListener("input", () => { itemButton().add(itemsLoc.value, "items-selections", 2); }); /** * Add a item button for tags. */ document.querySelector("#addTag").addEventListener("click", () => { let val = document.querySelector("#tags").value; itemButton().add(val, "tags-selection", 10); document.querySelector("#tags").value = ""; }); document.querySelector("#tags").addEventListener("keypress", (e) => { if (e.key === "Enter") { let val = document.querySelector("#tags").value; itemButton().add(val, "tags-selection", 10); document.querySelector("#tags").value = ""; } }); /** * Log all selected items in console. */ document.querySelector("#submit").addEventListener("click", () =>{ console.log("items-selections", itemButton().list("items-selections")); console.log("tags-selection", itemButton().list("tags-selection")); }) </script> </body> </html> |
And the resources > items.js file for the select element.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
export const items = [ "Cheese", "Cake", "Bananas", "Pizza", "Donuts", "Steak", "Cucumber", "Meat Pies", "Peaches", "Hamburgers", "Asparagus", ]; |
The Wrap Up
I hope you found this small library useful for creating your own buttons. You may wish to make style changes to your buttons to match your own colour theme.
You may also wish to extend or reduce the length of characters for each item. You can do this in the input.js file on line 37.
You can download a copy of the example here:
I really like hearing how people apply these components to their own projects. Feel free to share in the comments below.
If you found this tool useful, please click the like button so I know that I am making good content. Or if you want to get updates on my latest posts, please subscribe (below the comments).
~Yagi