Skip to main content

Overview

JSelect<T> is a versatile dropdown select component with support for single and multiple selection, searchable options, custom item rendering, and automatic filtering. It extends StackPane and provides a modern, styled alternative to JavaFX’s standard ComboBox.

Generic Type

T
type parameter
The type of items in the select list. Can be any object type.

Constructor

JSelect()
constructor
Creates a new JSelect with default configuration (single selection, not searchable).
JSelect<String> select = new JSelect<>();

Methods

Item Management

getItems()
ObservableList<T>
Gets the observable list of items in the select.
select.getItems().addAll("Option 1", "Option 2", "Option 3");

Selection (Single Mode)

selectedItemProperty()
ObjectProperty<T>
Gets the selected item property for binding.
select.selectedItemProperty().addListener((obs, old, newVal) -> {
    System.out.println("Selected: " + newVal);
});
getSelectedItem()
T
Gets the currently selected item (single selection mode).
String selected = select.getSelectedItem();
setSelectedItem(T item)
void
Sets the selected item (single selection mode).
select.setSelectedItem("Option 2");

Selection (Multiple Mode)

getSelectedItems()
ObservableList<T>
Gets the observable list of selected items (multiple selection mode).
ObservableList&lt;String&gt; selected = select.getSelectedItems();
System.out.println("Selected count: " + selected.size());

Configuration

multipleProperty()
BooleanProperty
Gets the multiple selection property for binding.
isMultiple()
boolean
Checks if multiple selection mode is enabled.
setMultiple(boolean multiple)
void
Enables or disables multiple selection mode.
select.setMultiple(true);
searchableProperty()
BooleanProperty
Gets the searchable property for binding.
isSearchable()
boolean
Checks if search functionality is enabled.
setSearchable(boolean searchable)
void
Enables or disables the search field in the dropdown.
select.setSearchable(true);
setPlaceholder(String placeholder)
void
Sets the placeholder text shown when no item is selected.
select.setPlaceholder("Choose a country...");
setConverter(Function<T, String> converter)
void
Sets a function to convert items to display strings.
select.setConverter(user -> user.getFirstName() + " " + user.getLastName());
setCellFactory(Callback<ListView<T>, ListCell<T>> factory)
void
Sets a custom cell factory for advanced item rendering (icons, custom layouts, etc.).
select.setCellFactory(param -> new CustomListCell());

Style Classes

j-select
CSS class
Applied to the root StackPane container
select-trigger
CSS class
Applied to the clickable trigger area
select-prompt
CSS class
Applied to the placeholder label
select-value
CSS class
Applied to the selected value label
select-tag
CSS class
Applied to individual tags in multiple selection mode
select-arrow
CSS class
Applied to the dropdown arrow icon
select-popup
CSS class
Applied to the popup dropdown container
select-search-container
CSS class
Applied to the search field container
select-list
CSS class
Applied to the ListView of items

Pseudo-classes

:showing
pseudo-class
Applied to the select when the dropdown popup is visible

Usage Examples

Basic String Select

import com.jjarroyo.components.JSelect;
import javafx.scene.layout.VBox;

public class BasicSelectExample {
    public VBox createCountrySelect() {
        VBox container = new VBox(10);
        
        JSelect<String> countrySelect = new JSelect<>();
        countrySelect.setPlaceholder("Select a country");
        countrySelect.getItems().addAll(
            "United States",
            "Canada",
            "Mexico",
            "Brazil"
        );
        
        // Listen to selection changes
        countrySelect.selectedItemProperty().addListener((obs, old, newVal) -> {
            System.out.println("Selected country: " + newVal);
        });
        
        container.getChildren().add(countrySelect);
        return container;
    }
}

Searchable Select

import com.jjarroyo.components.JSelect;

public class SearchableSelectExample {
    public JSelect<String> createCitySelect() {
        JSelect<String> citySelect = new JSelect<>();
        citySelect.setPlaceholder("Search cities...");
        citySelect.setSearchable(true);
        
        // Add many items
        citySelect.getItems().addAll(
            "New York", "Los Angeles", "Chicago", "Houston",
            "Phoenix", "Philadelphia", "San Antonio", "San Diego",
            "Dallas", "San Jose", "Austin", "Jacksonville"
        );
        
        return citySelect;
    }
}

Multiple Selection

import com.jjarroyo.components.JSelect;
import javafx.collections.ListChangeListener;

public class MultipleSelectExample {
    public JSelect<String> createSkillsSelect() {
        JSelect<String> skillsSelect = new JSelect<>();
        skillsSelect.setPlaceholder("Select skills");
        skillsSelect.setMultiple(true);
        skillsSelect.setSearchable(true);
        
        skillsSelect.getItems().addAll(
            "Java", "JavaScript", "Python", "C++",
            "React", "Angular", "Vue.js", "Node.js"
        );
        
        // Listen to selection changes
        skillsSelect.getSelectedItems().addListener(
            (ListChangeListener.Change<? extends String> c) -> {
                System.out.println("Selected skills: " + skillsSelect.getSelectedItems());
            }
        );
        
        return skillsSelect;
    }
}

Custom Object Select

import com.jjarroyo.components.JSelect;

public class User {
    private int id;
    private String firstName;
    private String lastName;
    private String email;
    
    // Constructor, getters, setters...
    public User(int id, String firstName, String lastName, String email) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }
    
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public String getEmail() { return email; }
}

public class UserSelectExample {
    public JSelect<User> createUserSelect() {
        JSelect<User> userSelect = new JSelect<>();
        userSelect.setPlaceholder("Select a user");
        userSelect.setSearchable(true);
        
        // Custom converter to display full name
        userSelect.setConverter(user -> 
            user.getFirstName() + " " + user.getLastName()
        );
        
        // Add users
        userSelect.getItems().addAll(
            new User(1, "John", "Doe", "john@example.com"),
            new User(2, "Jane", "Smith", "jane@example.com"),
            new User(3, "Bob", "Johnson", "bob@example.com")
        );
        
        // Handle selection
        userSelect.selectedItemProperty().addListener((obs, old, newVal) -> {
            if (newVal != null) {
                System.out.println("Selected: " + newVal.getEmail());
            }
        });
        
        return userSelect;
    }
}

Select with Custom Cell Factory

import com.jjarroyo.components.JSelect;
import javafx.scene.control.ListCell;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

public class CustomCellSelectExample {
    public JSelect<User> createUserSelectWithIcons() {
        JSelect<User> userSelect = new JSelect<>();
        userSelect.setPlaceholder("Select a user");
        userSelect.setSearchable(true);
        
        // Custom converter
        userSelect.setConverter(user -> 
            user.getFirstName() + " " + user.getLastName()
        );
        
        // Custom cell factory with email subtitle
        userSelect.setCellFactory(param -> new ListCell<User>() {
            @Override
            protected void updateItem(User user, boolean empty) {
                super.updateItem(user, empty);
                if (empty || user == null) {
                    setGraphic(null);
                    setText(null);
                } else {
                    VBox content = new VBox(2);
                    Label nameLabel = new Label(
                        user.getFirstName() + " " + user.getLastName()
                    );
                    nameLabel.setStyle("-fx-font-weight: bold;");
                    
                    Label emailLabel = new Label(user.getEmail());
                    emailLabel.setStyle("-fx-font-size: 11px; -fx-text-fill: gray;");
                    
                    content.getChildren().addAll(nameLabel, emailLabel);
                    setGraphic(content);
                }
            }
        });
        
        userSelect.getItems().addAll(
            new User(1, "John", "Doe", "john@example.com"),
            new User(2, "Jane", "Smith", "jane@example.com")
        );
        
        return userSelect;
    }
}

Programmatic Selection

JSelect<String> select = new JSelect<>();
select.getItems().addAll("Option 1", "Option 2", "Option 3");

// Set initial selection
select.setSelectedItem("Option 2");

// For multiple selection
JSelect<String> multiSelect = new JSelect<>();
multiSelect.setMultiple(true);
multiSelect.getItems().addAll("A", "B", "C", "D");
multiSelect.getSelectedItems().addAll("A", "C");

Behavior Details

Single Selection Mode

  • Click on an item to select it
  • Previously selected item is replaced
  • Popup closes after selection
  • Selected value displays in the trigger area

Multiple Selection Mode

  • Click items to toggle selection (checkboxes shown)
  • Multiple items can be selected
  • Popup remains open after selection
  • Selected items display as tags in trigger area
  • Click outside or press Escape to close

Search Functionality

  • Search field appears at top of dropdown when enabled
  • Filters items based on converter output
  • Case-insensitive matching
  • Updates in real-time as you type
  • Matches width of the trigger
  • Maximum height of 200px with scrolling
  • Auto-hides when clicking outside
  • Syncs stylesheets and dark mode from parent scene
  • Positioned 1px below trigger for seamless appearance

See Also

Build docs developers (and LLMs) love