Clear prompt text in JavaFX TextField only when user starts typing

13,957

Solution 1

Solution

This example will allow TextFields in JavaFX whose prompt behaviour is to show the prompt when the field is empty, even if the field has focus. The solution is a combination of custom CSS and a custom TextField class, which manipulates the css styles of the TextField.

promptpersist

persistent-prompt.css

.persistent-prompt:focused {
    -fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
}
.no-prompt {
    -fx-prompt-text-fill: transparent !important;
}

PersistentPromptTextField.java

import javafx.scene.control.TextField;

public class PersistentPromptTextField extends TextField {
    PersistentPromptTextField(String text, String prompt) {
        super(text);
        setPromptText(prompt);
        getStyleClass().add("persistent-prompt");
        refreshPromptVisibility();

        textProperty().addListener(observable -> refreshPromptVisibility());
    }

    private void refreshPromptVisibility() {
        final String text = getText();
        if (isEmptyString(text)) {
            getStyleClass().remove("no-prompt");
        } else {
            if (!getStyleClass().contains("no-prompt")) {
                getStyleClass().add("no-prompt");
            }
        }
    }

    private boolean isEmptyString(String text) {
        return text == null || text.isEmpty();
    }
}

PromptChanger.java

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class PromptChanger extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        TextField textField1 = new PersistentPromptTextField("", "First name");
        TextField textField2 = new PersistentPromptTextField("", "Last name");

        VBox layout = new VBox(
                10,
                textField1,
                textField2
        );
        layout.setPadding(new Insets(10));

        Scene scene = new Scene(layout);
        scene.getStylesheets().add(
                getClass().getResource(
                        "persistent-prompt.css"
                ).toExternalForm()
        );
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

How prompt handling is currently implemented in JavaFX 8

Default CSS for JavaFX 8 (modena.css) for controlling the prompt text is as follows:

.text-input {
    -fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
}
.text-input:focused {
    -fx-prompt-text-fill: transparent;
}

This will make the prompt text transparent whenever a field is focused, even if the field has no data in it.

In Comparison to HTML

HTML input has a placeholder, which is specified as follows:

User agents should present this hint to the user . . . when the element's value is the empty string or the control is not focused (or both).

You can try this functionality in your browser at this test link.

I think the argument against this behaviour for JavaFX "Prompt text should be cleared when focus is grabbed to signal readiness to receiving user input" is moot because focused text fields get a clearly visible focus ring, so the user knows that the control is ready to receive input even when the prompt text is displayed.

I think JavaFX should by default operate the same way as most HTML user agents in this respect. Feel free to create a Tweak request in the JavaFX issue tracker to request that the input prompt in JavaFX work similarly to HTML implementations (if there isn't an issue created for this already).

Alternative

The third party Gluon Glisten has a custom TextField control can have the following attributes:

  • Float Text- A place holder text inside a TextField which is transitioned to the top of the TextField when focus is received by it.

The nice thing about the Glisten based TextField is that the prompt text is always visible, whether or not the user has typed anything into the control.

gluon rest

gluon active

Solution 2

I know it's a bit old, but I needed it myself and this is still very relevant.
I will complete jewelsea's answer and give a simpler solution.

Background

Apparently this was the default behavior of Java(FX) (prompt text in a TextField was cleared only when the user starts typing).
But then, following a request (or a bug report) in the JIRA system,
Java changed this behavior (and made the default to clear the text when the TextField gets focus).

You can review this bug report here.


Solution

To get back to the old default (and nicer behavior IMO), all you need to do is to add the following line(s) of code.

In case your application interface is written using proper Java code.

Java Code:

textField.setStyle("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);");

Where textField is your TextField component.


And in case your application interface is written using FXML and CSS, add the following to your CSS file.

JavaFX FXML (CSS):

.text-input, .text-input:focused {
    -fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);
}

--------------------------------------------------------------------------------

Cleared-on-Focus Behavior

Currently it's the default behavior (the text-field's prompt text is cleared when the text-field gets focus),
so you don't need to do anything to get this behavior,
but in case Java will decide to go back to the cleared-on-typing behavior,
and you want to get the cleared-on-focus behavior, this is how to:

In case of proper Java code - it's a bit tricky since you can't define behavior of pseudo-classes directly.

Java Code (using bindings):

textField.styleProperty().bind(
        Bindings
        .when(textField.focusedProperty())
            .then("-fx-prompt-text-fill: transparent;")
            .otherwise("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);"));

or -

Java Code (using events):

textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
    @Override
    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
        if (newValue) {
            textField.setStyle("-fx-prompt-text-fill: transparent;");
        } else {
            textField.setStyle("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);");
        }
    }
});

JavaFX FXML CSS:

Add the following to your CSS file.

.text-input {
    -fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);
}

.text-input:focused {
    -fx-prompt-text-fill: transparent;
}

Hope this helped...

Solution 3

You could configure the '-fx-prompt-text-fill' key in '.text-field:focused' area like in '.text-field' area (css):

`.text-field {
    -fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
}
.text-field:focused {
    -fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
}`

and add the 'promptText' entry in fxml file:

<TextField fx:id="XY_Id" promptText="First name">

That is all and works for me.

Solution 4

Assuming you're setting prompt text via:

    @FXML TextField tf;

    public void someMethod() {
        tf.setPromptText("This is the prompt text");
    }

You can instead use tf.setText(String str) to set initial text.

Then, in the initialize method of your controller, add:

    tf.setOnKeyTyped(new EventHandler<KeyEvent>() {

        @Override
        public void handle(KeyEvent event) {
            tf.setText(null);

        }
    });

A link that you can refer to for additional help: Click me!

Solution 5

I am sure there may be several approaches and tricks to achieve what you're asking for, but, you are overriding a default, expected and standard UI behavior when you do so. Prompt text should be cleared when focus is grabbed to signal readiness to receiving user input, not after user starts typing.

Share:
13,957
Ramon
Author by

Ramon

Updated on June 05, 2022

Comments

  • Ramon
    Ramon almost 2 years

    The default behaviour is that the prompt text in the field is erased as the field is being focused. That is when the marker is in the field.

    Is it possible to configure the textfield so the prompt text is only erased when the user have started typing?

    Otherwise I need to add a label beside/over each textfield for description of the value in it.

  • Ramon
    Ramon almost 10 years
    Currently I've set the prompt text in SceneBuilder. I can try your proposed code.
  • Ramon
    Ramon almost 10 years
    Yes, I am fully aware of that and don't want to override a default behaviour myself... I think I will use labels beside/above the textfields instead and drop all prompt text's.
  • Prateek Mehta
    Prateek Mehta over 3 years
    promptText solved my problem, I coudn't find it anywhere else. Thanks a lot !