Firefox doesn't style :empty elements

Viewed 36

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.

2 Answers

Firefox has incompatibility with td:empty not because there is an issue with the css engine but because the way Firefox handles contenteditable is by adding a br tag into the region.

enter image description here

An alternate way to do this would be to change the html to use inputs that you disable when content is present.

table {
  border-collapse: collapse;
}

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

table input {
  border: none;
}

[placeholder] {
  color: gray;
  font-size: .9rem;
}
<table>
  <thead>
    <tr>
      <th>Forename</th>
      <th>Surname</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><input placeholder="Forename"></td>
      <td><input placeholder="Surname"></td>
    </tr>
    <tr>
      <td><input placeholder="Forename" value="John" disabled></td>
      <td><input placeholder="Forename" value="Doe" disabled></td>
    </tr>
  </tbody>
</table>

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>

Related