How to remove a CSS class from a Wicket component?
Asked Answered
N

4

16

It's pretty straightforward to dynamically add a CSS class to a component in Java code using an AttributeAppender:

component.add(new AttributeAppender("class", true, new Model<String>("foo"), " "));

Or, if you've extracted the above into an appropriate utility method or class, simply something like:

component.add(WicketUtils.cssClassAppender("foo"));

But how can I remove a CSS class?

You can easily remove all CSS classes by clearing the class attribute altogether:

component.add(new SimpleAttributeModifier("class", ""));

...but that is not acceptable if the component has other CSS classes that you do not wish to remove.

Wicket 1.4 (but feel free to post advice specific to later versions too).

Neurogram answered 13/4, 2012 at 5:40 Comment(1)
Not useful, eh - care to elaborate, downvoter? IMHO it's useful (for myself and others) to document non-immediately-obvious aspects of Wicket here on SO.Neurogram
N
18

Here's one way I came up with:

public class CssClassRemover extends AttributeModifier {
    public CssClassRemover(String cssClass) {
        super("class", false, new Model<String>(cssClass));
    }

    @Override
    protected String newValue(String currentValue, String valueToRemove) {
        // NB: naive approach; breaks with e.g. "foo foo-bar" & "foo"
        return currentValue.replaceAll(valueToRemove, "");
    }
}

The code that uses the above helper would then be:

component.add(new CssClassRemover("foo"))

(Of course you can also just create anonymous AttributeModifier subclasses as needed, but putting the logic in a separate utility class or method cleans it up a lot.)

Edit: An improved version of newValue() that handles corner cases better (see comment by biziclop). NB: uses Guava. (You're welcome to post simpler (regex?) versions.)

@Override
protected String newValue(String currentValue, String valueToRemove) {
    if (currentValue == null) return "";

    Set<String> classes = Sets.newHashSet(Splitter.on(" ").split(currentValue));
    classes.remove(valueToRemove);
    return Joiner.on(" ").join(classes); 
}
Neurogram answered 13/4, 2012 at 5:45 Comment(5)
Feel free to post better / simpler ways to do this. Specifically if I overlooked something like this that comes with Wicket built-in...Neurogram
Depending on the use case it could also be suitable to use an AttributeModifier and decide which classes to use in an IModel.Magda
You should add whitespace (and string start/end) matching to the beginning and the end of valueToRemove, just consider the case where class="foo foo-bar" Even better if you use currentValue.split( "\\s+" ) to obtain all the individual classes and then re-append them without the one you want to remove. For a bonus point, you can also do the same to valueToRemove to be able to remove several classes with a single CssClassRemover.Jamison
@biziclop: Good call! I'm not a big fan of regex minutiae, so I quickly coded a Guava version that should be pretty robust. If anyone doesn't like it, competing implementations are welcome. :-)Neurogram
Well, if you're using Guava anyway, it's easy. :)Jamison
L
3

Building on Jonik's answer, the following adds negative lookahead to ignore occurrences in the middle of a different style class (and is case insensitive).

public class StyleClassRemover extends AttributeModifier {

    public StyleClassRemover(final String cssClass) {
        super("class", false, Model.of(cssClass));
    }

    @Override
    protected String newValue(final String currentValue, final String valueToRemove) {
        if (currentValue == null) {
            return "";
        }

        final String patternString = "(^|\\s+)" + Pattern.quote(valueToRemove) + "(?!\\S)";
        return Pattern.compile(patternString, Pattern.CASE_INSENSITIVE).matcher(currentValue).replaceAll("");
    }
}

Tested input: http://fiddle.re/ah0ca6

Leary answered 8/5, 2015 at 13:28 Comment(0)
I
0

Wicket 1.5+ has org.apache.wicket.AttributeModifier#remove()

Ionium answered 13/4, 2012 at 14:24 Comment(1)
Hmm, looking at the javadoc ("Creates a attribute modifier that removes an attribute with the specified name"), it doesn't seem to do what I want... Sounds like it would remove the "class" attribute entirely, just like the SimpleAttributeModifier example in the question.Neurogram
C
0

Not sure when it was introduced, but Wicket has ClassAttributeModifier()

component.add(new ClassAttributeModifier() {

    @Override
    protected Set<String> update(Set<String> oldClasses) {
        return oldClasses
            .stream()
            .filter(c -> !"foo".equals(c))
            .collect(Collectors.toSet());
    }

});
Carrefour answered 26/7 at 14:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.