What's an elegant way to conditionally add a class to an HTML element in a view?

Ruby on-RailsViews

Ruby on-Rails Problem Overview


I occasionally have to add a class to an html element based on a condition. The problem is I can't figure out a clean way of doing it. Here's an example of the stuff I've tried:

<div <%= if @status = 'success'; "class='ok'"; end %>>
   some message here
</div>

OR

<% if @status == 'success' %>
   <div class='success'>
<% else %>
   <div>
<% end %>
   some message here
</div>

I don't like the first approach because it's crowded looking and hard to read. I don't like the second approach because the nesting is screwed up. It'd be nice to put it in the model, (something like @status.css_class), but that doesn't belong there. What do most people do?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

I use the first way, but with a slightly more succinct syntax:

<div class="<%= 'ok' if @status == 'success' %>">

Though usually you should represent success with boolean true or a numeric record ID, and failure with boolean false or nil. This way you can just test your variable:

<div class="<%= 'ok' if @success %>">

A second form using the ternary ?: operator is useful if you want to choose between two classes:

<div class="<%= @success ? 'good' : 'bad' %>">

Finally, you can use Rail's record tag helpers such as div_for, which will automagically set your ID and class based on the record you give it. Given a Person with id 47:

# <div id="person_47" class="person good">
<% div_for @person, class: (@success ? 'good' : 'bad') do %>
<% end %>

Solution 2 - Ruby on-Rails

Avoiding logic in the views

The problem with the standard approach is that it requires logic in the form of if statements or ternaries in the view. If you have multiple conditional CSS classes mixed with default classes, then you need to put that logic into a string interpolation or ERB tag.

Here's an updated approach that avoids putting any logic into the views:

<div class="<%= class_string(ok: @success) %>">
   some message here
</div>

class_string method

The class_string helper takes a hash with key/value pairs consisting of CSS class name strings and boolean values. The result of the method is a string of classes where the boolean value evaluated to true.

Sample Usage
class_names(foo: true, bar: false, baz: some_truthy_variable)
# => "foo baz"
Other Use Cases

This helper can be used within ERB tags or with Rails helpers such as link_to.

<div class="<%= class_string(ok: @success) %>">
   some message here
</div>

<% div_for @person, class: class_string(ok: @success) do %>
<% end %>

<% link_to "Hello", root_path, class: class_string(ok: @success) do %>
<% end %>
Either/Or Classes

For use cases where a ternary would be necessary (e.g. @success ? 'good' : 'bad'), pass an array where the first element is the class for true and the other is for false

<div class="<%= [:good, :bad] => @success %>">

Inspired by React

This technique is inspired by an add-on called classNames (formerly known as classSet) from Facebook’s React front-end framework.

Using in your Rails projects

As of now, the class_names function does not exist in Rails, but this article shows you how to add or implement it into your projects.

Solution 3 - Ruby on-Rails

class_names (Rails 6.1+)

Rails 6.1 introduces a class_names view helper to make it easier to conditionally apply class names in views.

Before:

<div class="<%= item.for_sale? ? 'active' : '' %>">

After:

<div class="<%= class_names(active: item.for_sale?) %>">

More examples:

class_names("foo", "bar")
# => "foo bar"
class_names({ foo: true, bar: false })
# => "foo"
class_names(nil, false, 123, "", "foo", { bar: true })
# => "123 foo bar"

Sources:

Solution 4 - Ruby on-Rails

You can also use the content_for helper, especially if the DOM is located in a layout and you want to set the css class depending on the partial loaded.

On the layout:

%div{class: content_for?(:css_class) ? yield(:css_class) : ''}

On the partial:

- content_for :css_class do
    my_specific_class

That is it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionryeguyView Question on Stackoverflow
Solution 1 - Ruby on-Railsuser229044View Answer on Stackoverflow
Solution 2 - Ruby on-RailsCarlos Ramirez IIIView Answer on Stackoverflow
Solution 3 - Ruby on-RailsMarian13View Answer on Stackoverflow
Solution 4 - Ruby on-RailsacmouneView Answer on Stackoverflow