HTL Tutorial #10: data-sly-attribute - Manipulating HTML Attributes

HTL Tutorial #10: data-sly-attribute - Manipulating HTML Attributes

What is data-sly-attribute?

data-sly-attribute allows you to dynamically manipulate HTML attributes: add, modify, or remove attributes based on conditions or data.

Basic Syntax

Single Attribute

<div data-sly-attribute.attributeName="${value}">
  Content
</div>

Multiple Attributes

<div data-sly-attribute="${object}">
  Content
</div>

Simple Examples

Adding an Attribute

<!-- Adds data-id dynamically -->
<div data-sly-attribute.data-id="${product.id}">
  ${product.name}
</div>

Output:

<div data-id="12345">
  Laptop
</div>

Modifying Existing Attribute

<!-- Override class attribute -->
<div class="card" data-sly-attribute.class="${'card ' + product.status}">
  Content
</div>

Output (if product.status = 'active'):

<div class="card active">
  Content
</div>

Removing an Attribute

If the value is null, false, or an empty string, the attribute is removed:

<div title="Default title"
     data-sly-attribute.title="${properties.customTitle}">
  Hover me
</div>

If properties.customTitle is null:

<div>
  Hover me
</div>

If properties.customTitle = 'Custom':

<div title="Custom">
  Hover me
</div>

Multiple Attributes with Object

You can pass an object to set multiple attributes at once:

<div data-sly-use.attrs="com.example.AttributesModel"
     data-sly-attribute="${attrs.htmlAttributes}">
  Content
</div>

Java Model:

@Model(adaptables = Resource.class)
public class AttributesModel {

    public Map<String, String> getHtmlAttributes() {
        Map<String, String> attrs = new HashMap<>();
        attrs.put("data-id", "123");
        attrs.put("data-type", "product");
        attrs.put("role", "article");
        return attrs;
    }
}

Output:

<div data-id="123" data-type="product" role="article">
  Content
</div>

Common Patterns

1. Conditional CSS Classes

<div class="card"
     data-sly-attribute.class="${'card ' +
                                 (product.featured ? 'featured ' : '') +
                                 (product.onSale ? 'on-sale' : '')}">
  ${product.name}
</div>

Output (featured=true, onSale=false):

<div class="card featured">
  Laptop
</div>

2. Dynamic Data Attributes

<div data-sly-use.product="com.example.ProductModel"
     data-sly-attribute.data-product-id="${product.id}"
     data-sly-attribute.data-price="${product.price}"
     data-sly-attribute.data-category="${product.category}">
  ${product.name}
</div>

Output:

<div data-product-id="123" data-price="99.99" data-category="electronics">
  Laptop
</div>

3. ARIA Attributes for Accessibility

<button data-sly-attribute.aria-expanded="${menu.isOpen}"
        data-sly-attribute.aria-controls="${menu.panelId}">
  Menu
</button>

<div data-sly-attribute.id="${menu.panelId}"
     data-sly-attribute.aria-hidden="${!menu.isOpen}">
  <!-- Menu content -->
</div>

4. Link with Conditional Target

<a href="${link.url}"
   data-sly-attribute.target="${link.external ? '_blank' : ''}"
   data-sly-attribute.rel="${link.external ? 'noopener noreferrer' : ''}">
  ${link.title}
</a>

Output (external link):

<a href="https://example.com" target="_blank" rel="noopener noreferrer">
  Visit example
</a>

Output (internal link - attributes removed):

<a href="/about">
  About us
</a>

Combining with Other Statements

With data-sly-test

<div data-sly-test="${properties.customId}"
     data-sly-attribute.id="${properties.customId}">
  <!-- Only if customId exists, the attribute is added -->
</div>

With data-sly-list

<ul data-sly-list.item="${properties.items}">
  <li data-sly-attribute.class="${itemList.first ? 'first' : ''}
                                 ${itemList.last ? 'last' : ''}">
    ${item}
  </li>
</ul>

HTML5 Boolean Attributes

For HTML5 boolean attributes like disabled, checked, readonly:

<!-- ✓ CORRECT - use true/false -->
<input type="checkbox"
       data-sly-attribute.checked="${user.subscribed}">

<!-- If user.subscribed = true -->
<input type="checkbox" checked>

<!-- If user.subscribed = false -->
<input type="checkbox">
<button data-sly-attribute.disabled="${!product.available}">
  Buy
</button>

<!-- If available = false -->
<button disabled>Buy</button>

<!-- If available = true -->
<button>Buy</button>

Complete Example: Image Component

<div data-sly-use.image="com.example.ImageModel" class="image-wrapper">
  <img src="${image.src}"
       data-sly-attribute.alt="${image.alt}"
       data-sly-attribute.title="${image.title}"
       data-sly-attribute.width="${image.width}"
       data-sly-attribute.height="${image.height}"
       data-sly-attribute.loading="${imageList.first ? '' : 'lazy'}"
       data-sly-attribute.class="${'responsive-image ' + (image.rounded ? 'rounded' : '')}"
       data-sly-attribute.data-caption="${image.caption}">

  <!-- Caption only if present -->
  <p data-sly-test="${image.caption}" class="caption">
    ${image.caption}
  </p>
</div>

Output (with all data):

<div class="image-wrapper">
  <img src="/content/dam/image.jpg"
       alt="Image description"
       title="Image title"
       width="800"
       height="600"
       loading="lazy"
       class="responsive-image rounded"
       data-caption="Photo taken in 2025">

  <p class="caption">Photo taken in 2025</p>
</div>

Complete Example: Form Input

<div data-sly-use.field="com.example.FormFieldModel">
  <input type="${field.type}"
         name="${field.name}"
         data-sly-attribute.id="${field.id}"
         data-sly-attribute.value="${field.value}"
         data-sly-attribute.placeholder="${field.placeholder}"
         data-sly-attribute.required="${field.required}"
         data-sly-attribute.disabled="${field.disabled}"
         data-sly-attribute.minlength="${field.minLength}"
         data-sly-attribute.maxlength="${field.maxLength}"
         data-sly-attribute.pattern="${field.pattern}"
         data-sly-attribute.aria-label="${field.label}"
         data-sly-attribute.aria-describedby="${field.hasError ? field.errorId : ''}"
         data-sly-attribute.class="${'form-control ' + (field.hasError ? 'is-invalid' : '')}">

  <!-- Error message -->
  <div data-sly-test="${field.hasError}"
       data-sly-attribute.id="${field.errorId}"
       class="invalid-feedback">
    ${field.errorMessage}
  </div>
</div>

Special Attributes

href and src - URI Context

HTL automatically applies context='uri' to href and src:

<a href="${link.url}">Link</a>
<!-- Automatically protected against javascript:, data:, etc -->

<!-- Equivalent to: -->
<a data-sly-attribute.href="${link.url @ context='uri'}">Link</a>

style Attribute

<div data-sly-attribute.style="${'background-color: ' + theme.bgColor +
                                  '; color: ' + theme.textColor}">
  Styled content
</div>

Warning: Prefer CSS classes when possible!

Best Practices

  1. Remove empty attributes: HTL does this automatically with null/false/""
  2. Automatic context: No need to specify @ context='uri' for href/src
  3. Boolean attributes: Use true/false directly
  4. CSS classes: Prefer CSS classes over inline styles
  5. ARIA attributes: Use for accessibility
  6. Data attributes: Perfect for JavaScript hooks
  7. Objects for multiple attributes: Cleaner when you have many attributes

Attribute Priority

If an attribute is defined both in HTML and with data-sly-attribute, data-sly-attribute wins:

<div class="original"
     data-sly-attribute.class="${'new-class'}">
  Content
</div>

<!-- Output: -->
<div class="new-class">
  Content
</div>

To preserve and add:

<div class="original"
     data-sly-attribute.class="${'original ' + additionalClass}">
  Content
</div>

Common Mistakes

❌ Incorrect attribute name

<!-- WRONG - use data-sly-attribute.name -->
<div data-sly-attribute="data-id"="${value}">

<!-- CORRECT -->
<div data-sly-attribute.data-id="${value}">

❌ Forgetting the dot

<!-- WRONG - missing the . -->
<div data-sly-attributeclass="${value}">

<!-- CORRECT -->
<div data-sly-attribute.class="${value}">

❌ Boolean attributes with strings

<!-- WRONG - use true/false -->
<input data-sly-attribute.disabled="${'disabled'}">

<!-- CORRECT -->
<input data-sly-attribute.disabled="${true}">

Practical Exercises

  1. Card Component: Create cards with dynamic attributes (id, data-*, class)
  2. Link List: List of links with target="_blank" only for external ones
  3. Form Builder: Form with validation and ARIA attributes
  4. Image Gallery: Gallery with lazy loading and dimensional attributes

Next Lesson

In the next lesson we'll explore data-sly-element to dynamically change the HTML tag of an element.


Lesson #10 of the HTL Tutorial series. ← Previous lesson | Next lesson →