Search code examples
cssfirefoxcontenteditable

Firefox doesn't style :empty elements


Here is a snippet with a sample code:

table {
  border-collapse: collapse;
}

th, td {
  border: 1px solid gray;
  padding: 3px 6px;
}

[contenteditable]:empty:not(:focus)::before {
  content: attr(data-placeholder);
  color: gray;
  font-size: .9rem;
}
<table>
  <thead>
    <tr>
      <th>Firstname</th>
      <th>Lastname</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td contenteditable="true" data-placeholder="Firstname"></td>
      <td contenteditable="true" data-placeholder="Lastname"></td>
    </tr>
    <tr>
      <td>John</td>
      <td>Doe</td>
    </tr>
  </tbody>
</table>

In Chrome and Safari, it works pretty much as expected:

enter image description here

For some reason, in Firefox, the contenteditable tds don't get the placeholder:

enter image description here

How can I fix this issue?


EDIT: It seems this is issue is more related to :empty than [contenteditable] as this code kinda works:

[contenteditable]:not(:focus)::before {
    content: attr(data-placeholder);
    color: gray;
    font-size: .9rem;
}

But then the placeholder is always shown, hence not being an actual "placeholder" anymore.


Solution

  • As @DreamTeK mentioned, Firefox seems to add a <br> in empty contenteditable elements. His answer, using input instead of contenteditable is valid.

    In case you have no choice but to use contenteditable, here is a fix in JS to remove this unwanted br:

    // VanillaJS
    document.addEventListener("DOMContentLoaded", () => {
        document.querySelectorAll('[contenteditable]').forEach((elt) => {
            if (elt.children.length === 1 && elt.firstChild.tagName === "BR") {
                elt.firstChild.remove();
            }
        })
    });
    
    // jQuery
    /*
    $(document).ready(() => {
        $('[contenteditable]').each((_, elt) => {
            if ($(elt).children().length === 1 && $(elt).has('br')) {
                $(elt).html('');
            }
        });
    });
    */
    table {
      border-collapse: collapse;
    }
    
    th, td {
      border: 1px solid gray;
      padding: 3px 6px;
    }
    
    [contenteditable]:empty:not(:focus)::before {
      content: attr(data-placeholder);
      color: gray;
      font-size: .9rem;
    }
    <table>
      <thead>
        <tr>
          <th>Firstname</th>
          <th>Lastname</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td contenteditable="true" data-placeholder="Firstname"></td>
          <td contenteditable="true" data-placeholder="Lastname"></td>
        </tr>
        <tr>
          <td>John</td>
          <td>Doe</td>
        </tr>
      </tbody>
    </table>