Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-selectors] Standardize :(‑moz‑)first‑node and :(‑moz‑)last‑node #3216

Open
ExE-Boss opened this issue Oct 16, 2018 · 16 comments

Comments

@ExE-Boss
Copy link
Contributor

ExE-Boss commented Oct 16, 2018

Right now, CSS Selectors 4 defines :first-child and :last-child, which works as such for the following:

span:last-child {
  background-color: lime;
}
<div>
  <span>This doesn’t match.</span>
  <span>This matches.</span>
</div>

<div>
  <span>This matches.</span>
  Some text after.
</div>

For :first-node and :last-node, this would work as follows:

span:last-node {
  background-color: lime;
}
<div>
  <span>This doesn’t match.</span>
  <span>This matches.</span>
  <!-- White-space and comments are ignored when
     - determining whether :last-node matches -->
</div>

<div>
  <span>This doesn’t match.</span>
  Some text after.
</div>

Mozilla Firefox already implements the previous in prefixed form and exposes it to the web.

@emilio
Copy link
Collaborator

emilio commented Oct 16, 2018

FWIW, these selectors are somewhat slow, and I planned on trying to hide them from content when I had the chance...

@upsuper
Copy link
Member

upsuper commented Oct 16, 2018

Selectors mainly work on element level, and extending them to node level may cause some confusing, and ignoring white-space and comments isn't really what "last node" should mean...

@ExE-Boss ExE-Boss reopened this Oct 16, 2018
@emilio
Copy link
Collaborator

emilio commented Oct 16, 2018

Selectors mainly work on element level, and extending them to node level may cause some confusing, and ignoring white-space and comments isn't really what "last node" should mean...

I agree, the only reason these selectors exist at all in Gecko is to implement quirks.

@SelenIT
Copy link
Collaborator

SelenIT commented Oct 16, 2018

The demand to differentiate child elements that don't have any text before them from those that do seems to occur quite often (especially in styling user-generated content, in designing components that can be inserted into arbitrary environment, some more use cases are mentioned in #2208). So it would be great if there would be a standard tool for this, even if the current Gecko's experimental implementation is not perfect (though it's much better than nothing:).

@ExE-Boss
Copy link
Contributor Author

ExE-Boss commented Oct 17, 2018

The demand to differentiate child elements that don't have any text before them from those that do seems to occur quite often (especially in styling user-generated content)

For me it’s elements that don’t have any text after them (eg. the last paragraph in a comment shouldn’t have a bottom margin, but I can’t guarantee that there won’t be any text after it, because I’m dealing with making a custom post style for my posts on forum using a horrible frontend that still uses tables for layout in 2018).

@fantasai
Copy link
Collaborator

As the main use case for this is controlling margins, we've added a 'margin-trim' property, see discussion in https://lists.w3.org/Archives/Public/www-style/2018Nov/0005.html

Are there other major use cases for this?

@ExE-Boss
Copy link
Contributor Author

ExE-Boss commented Nov 16, 2018

There are the same use cases as for :first‑child and :last‑child, but I don’t know how major those are.

@ExE-Boss ExE-Boss changed the title [css-selectors] Standardize :(-moz-)first-node and :(-moz-)last-node [css-selectors] Standardize :(‑moz‑)first‑node and :(‑moz‑)last‑node Nov 16, 2018
@simevidas
Copy link
Contributor

I don’t know how important my use-case is, but on my website, I style <strong> elements differently (small caps, extra bold) if they appear before any other text in a paragraph.

Screen Shot 2019-09-22 at 5 23 27 PM

Currently, I have to use JavaScript to add a CSS class to these elements, but I would use the :first-node selector instead if it was cross browser.

// add class to paragraphs that begin with a <strong> element
for (let p of article.querySelectorAll('p')) {
  let node = p.childNodes[0];
  if (node && node.nodeName === 'STRONG') {
    p.classList.add('Issue__note');
  }
}

@oriadam
Copy link

oriadam commented Mar 29, 2021

I have another use case: I style emojione images differently when they appear in the middle of text, than when they are solo.
image

@tabatkins
Copy link
Member

@emilio Can you elaborate on why these are slow? Presumably they're slow in comparison to :first-child? Is it related to the "ignore text nodes that are just whitespace" bit?

@brandonmcconnell
Copy link

brandonmcconnell commented Mar 31, 2021

I just needed this same selector for a project I was working on today. I would like to target a span tag that exists in a block of text. If it is not the first node, I would like to apply margin-left to space it away from the text to its left, and it is not the last node, I would like to apply margin-right to space it away from the text to its right. Unfortunately, this isn't currently possible in pure CSS, because one span tag inside a body of other text will match both :first-child and :last-child even though it is neither the first nor the last node.

This is true for all three examples below:

<!-- Example 1 -->
Lorem ipsum <span>TEST</span> lorem ipsum

<!-- Example 2 -->
<span>TEST</span> lorem ipsum lorem ipsum

<!-- Example 3 -->
Lorem ipsum lorem ipsum <span>TEST</span>

@kizu
Copy link
Member

kizu commented Jun 18, 2024

Another use case for this could be changing back the first abbreviation/number from oldstyle numerals and small caps when it is at the beginning of the paragraph. Currently, we don't have any way to know if some element is there. (motivated by the discussion from https://front-end.social/@kizu/112637529030687445, example CodePen: https://codepen.io/kizu/pen/JjqpKqK; ideally we'd want to actually target the first word/node in the sentence, but I don't know if we could ever achieve this with just CSS).

@ggedde
Copy link

ggedde commented Jul 18, 2024

For me, I am looking at styling these buttons differently:

<button><svg>...</svg> Click Here</button>
<button>Click Here <svg>...</svg></button>

But unfortunately there is no way to determine if the text is before or after the svg.
So maybe we could then do:
button:has( > svg:not(:last-node)) or button:has( > svg:not(first-node))
without it affecting:
<button><svg>...</svg></button>

@LeaVerou
Copy link
Member

LeaVerou commented Nov 5, 2024

I had just filed a new issue for this (#11154):

Currently, :*-child pseudo-classes only consider element children, and will match even if the element has text nodes before it. For example, in <button>foo <i>A</i> bar</button> :first-child, :last-child and :only-child will all match. For many use cases, it is important to match when the node is actually the first/last/only one.

I have not found use cases requiring :nth-node(), so I think the three titular pseudos should do it.

The use case that prompted it was very similar to the one mentioned: handling icons in the beginning of a button/menu item/tab/etc as a prefix icon, and if at the end as a suffix icon (slotting them into corresponding slots in components etc). Also, given that text nodes create anonymous boxes wrt grid/flex handling, it seems like the ship has sailed and we can't really keep pretending that text nodes don't exist for the purposes of CSS. 😁

@tats-u
Copy link

tats-u commented Nov 7, 2024

I want to remove the left margin only if there is no leading element.

.chrome-only {
  padding: 0.4em;
  background-color: #6633ff;
  margin-inline: 0.5em;
  color: white;
  font-size: 90%;
  border-radius: 0.6em;
  display: inline-block;
  font-family: sans-serif;
  font-weight: bold;
}

.chrome-only:-moz-first-node {
  margin-inline-start: 0;
}
<p>
  <span class="chrome-only">Chrome Only</span>You can do ...... Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<p lang="ja-jp">
  この機能は<span class="chrome-only">Chrome限定</span>です。
</p>

image

https://codepen.io/tats-u/pen/xxvyJRK

@tats-u
Copy link

tats-u commented Dec 3, 2024

Another example:

li > p:-moz-first-node,
dd > p:-moz-first-node {
  margin-block-start: 0;
}
li > p:-moz-last-node,
dd > p:-moz-last-node {
  margin-block-end: 0;
}
<ul>
  <li>1st item</li>
  <li>
    <p>2nd item</p>
    <ul>
        <li>Another</li>
        <li>List</li>
    </ul>
    <p>Extra description</p>
  </li>
  <li>3rd item</li>
</ul>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests