I should start off by saying that I don't really have an issue that I'm trying to work through. I just had an interesting thought about how Shadow Dom and the CSS3 :target selector might / should / currently do work together.
I know that HTML specification says that there should only ever be one element with a particular ID value in a valid HTML document. But when we start using webcomponents with shadow dom we could very easily find ourselves using multiple elements with the same ID. This is especially true when we use the same component multiple times in the same page. So the question that I have is this: what should happen to an element inside a shadow dom region that has an ID value which matches the current hash and which is styled with a :target rule?
For example, if I wrote a webcomponent (my-element) that contained
<style>
#debug {display:none}
#debug:target { display:block; background-color:yellow; border 2px solid red; }
</style>
<div id="debug">some debug data</div>
What should happen to all the instance of my-element that I put on a page and navigated to #debug on? Should the debug element in each component show? Should none of them show? Should only the first element's debug div show (the same one I'd expect the browser to try and navigate to)?
My opinion is that if the page does not have an element with an ID=debug value that no scrolling navigation should appear on the page. As shadow dom is isolated from the rest of the page's styles the browser shouldn't try to navigate to such an element nested in shadow dom. Each my-element instance should be able to see the current page's URL though and should apply any matching :target rules, such that each my-elements' debug div should be visible.
If this were the case it would make for some interesting possibilities for sharing page state across all components, such as the debug example above. However, I doubt that is how Chrome is currently implementing things. And I'm pretty sure this Shadow Dom polyfill isn't going to handle things correctly as it basically shoehorns everything into the page's Dom tree and that would break the html specification.
Just curious if anyone has an answer for how this should work and how it works today...
(edited from my pc to add formatting... hard to do from my phone)
I think you can see the shadow DOM like a nested document. CSS can't address elements inside the shadow DOM from the outside (previously existing shadow piercing CSS selectors were deprecated).
This also encapsulates ids and therefore multiple components that contain elements with an id won't cause collisions or duplicates.
If you have the CSS with the :target selector inside a components style, it should be able to address the element with the matching id, otherwise it shouldn't.
So the question that I have is this: what should happen to an element
inside a shadow dom region that has an ID value which matches the
current hash and which is styled with a :target rule?
Adding to Günter Zöchbauer above answer an alternative is to use the Custom Element object when the style is encapsulated, if the style is global it will work just fine. Use the define method to create a custom component as shown in the the docs. This will not encapsulate your elements so be aware that your styles can be shared across files.
So instead of doing this:
const shadow = this.attachShadow({ mode: "open" });
shadow.appendChild(pTag)
Use this:
this.appendChild(pTag);
both previous examples suppose you're in a HTMLElement class or a class that inherits it.
Related
After carefully reading all related articles and posts on many sites, there is still one remaining question: Can i have a single, exchangeable CSS for a web app built with web components widhtout having to deal with all the weird stuff suggested by W3C?
I know about ::part( something ) and exportparts="something" to access nested components, but that does not go down the tree, so I have to add a part attribute to almost every element, which totally bloats my HTML.
Having an #import rule in each component is also not a great option, because it would be one more HTTP request per stylesheet. Also, once loaded in a template, the importet css can not be exchanged easily.
W3C really makes our lives harder by removing /deep/ and ::shadow. I know, performance concerns, blah, blah, but at least that worked like a charm.
Possible solutions I find impractical:
How to style slotted parts from the parent downwards
::slotted CSS selector for nested children in shadowDOM slot
How to access elements inner two shadow dom
Example HTML where all nested elements would be styleable with global CSS:
<body>
<o-app>
#shadowDOM
<o-header exportparts="username:o-textinput__username,action-ok:o-action__action-ok,o-action__label" part="o-header">
#shadowDOM
<o-texinput part="username">
<o-action exportparts="label:o-action__label" part="action-ok">
#shadowDOM
<div part="label">
Then I can finally style the label div by selecting it with:
::part( o-action__label ) {}
Now tell me that having to specify every single part of all descendant elements in the parent elements is not a total mess!
Playaround on Codepen: https://codepen.io/5180/pen/jOyQNYq?editors=1111
Now in 2021 I would rather use light DOM only instead of forcing the shadow DOM to behave like its counterpart, because there is currently no easy method of piercing through the artificial boundary. It was in the spec – ::shadow and /deep/ – but got removed, so deal with it. ::theme() is not ready yet, The ::part() selector is useless for deep styling as I pointed out in my example.
Just use the light DOM (innerHTML) of your custom element to avoid deep styling/theming issues.
I'm trying to select <a> elements that are not the parents of <img> elements. (Note: if it's relevant some of the anchors I want to select are childless.) I tried this:
a > :not(img) {}
and this:
a:not(> img) {}
but neither of them seem to work. How would I accomplish this in CSS?
There is a spec, currently in draft, for a :has() pseudo-class. No browser supports it yet. If the spec is someday approved and implemented, you'd be able to do this:
a:not(:has(img)) {
// Styles
}
The MDN page says that :has would never work in stylesheets, only in JavaScript; but in saying that, it links to a section of the spec about a "dynamic selector profile" that apparently no longer exists.
I think the browser vendors typically have a problem with implementing CSS features that require knowledge of the DOM that only exists after the selected element is created, so I don't know if we should get our hopes up for this. Someone who follows the mailing lists or is generally smarter than me might offer a better prognosis.
Unfortunately, no. You'd need to use jQuery.
You could do some kind of workaround using CSS:
Assign a class to links that do not have child elements that are images and use that class to style the links as normal (e.g. a.class{color: red})
Assign a class to links that do have an image child element, and use a:not(.class){} to change their color
Reason: There is no parent selector in CSS. See:
Is there a CSS parent selector?, CSS Parent/Ancestor Selector
<div class="thumbnail-popular" style="background: url('http://images.gogoanime.tv/images/upload/Go!.Princess.Precure.full.1812487.jpg');"></div>
I am trying to get the url component of this div class but I seem to be unable to fetch that specific data in the div class.
I have looked into making use of attributes but my attempts have been unsuccessful so far.
Usage of this CSS selector is through Kimonolabs.
div.thumbnail-popular should get you the element you're looking for — unless there is more than one such element, in which case you will need to narrow down your selector.
For example you will need to find out if this particular element belongs to a specific parent, or is the first, second, ... nth child, or any other information about the surrounding elements in the page that you're working with.
The background URL is in a style attribute on this element, so you will need to extract that attribute as described here. However you will still need to parse the declarations inside the style value in order to get the URL; I am not sure if it is possible to do this through kimono as I am not familiar with it (I'm not sure what its advanced mode really does, and it's difficult to tell from the lone screenshot that is provided in that help article).
considering I have a single css file for my entire website (and that doing so is an usual technique), I was wondering if there is a way to select website-wide attributes like body (or any other attribute in fact) according to the current page using only css.
Something like
body:in('index.html') {
some properties;
}
body:in('contact.html') {
other properties;
}
Again, css only. I know the simple solutions using things like php, js, jquery...
Selectors have no information about the document beyond what is presented in the DOM tree, and the DOM does not expose information about the page according to its file name, URL, or any other such properties.
Historically there was a #document at-rule for querying the current URL, but it was removed from the Conditional Rules Level 3 module. The most likely reason for this is the lack of cross-vendor implementations, as the only known implementation exists in Gecko, as #-moz-document. It's so bad, that the only uses for it that you'll spot in the wild are not to apply CSS based on a certain page, but solely as a CSS hack for Firefox.
As you've stated, the simplest workaround for this is to have each page apply a unique class to the html or body element and select according to that class, whether via hardcoding, or dynamically.
you could add and id atribute to the body tag and style things inside it using:
body#contact div{
background:#376;
color:#857;
/*etc*/
}
more information about selectors in http://www.w3.org/TR/css3-selectors/
According to the Mozilla Doc, the value none for the CSS attribute display does the following:
Turns off the display of an element (it has no effect on layout); all descendant elements also have their display turned off. The document is rendered as though the element did not exist.
I know this means I can't see the element. Since the element has no effect on the layout, it appears as if it does not exist.
My question is: does it still exist in the layout (it still responds to user events)? Or does it not exist in the layout at all (therefore not just appearance)?
Just to clarify:
I know the element still exists in the DOM. I'm asking if interaction with the view can still affect the state of that element. For example, if I click where a hidden element would have existed, does that still trigger an event?
I'm asking because I know you can target hidden elements in CSS like so:
input[type="checkbox"] {
display: none;
}
input[type="checkbox"]:checked + otherElement {
...
}
Some event must be firing or else the second CSS selector would not work.
Can someone explain this?
For example, if I click where a hidden element would have existed,
does that still trigger an event?
There is no such thing as "click where a hidden element would have existed".
For example, if a brick was not used in building a house, you can't touch it by touching the line between two other bricks, even if it could have been there otherwise.
So there are no user-caused events that apply to an invisible object not included in the layout.
However I am pretty sure it would still produce other events, for example ones reported by MutationObserver. I would expect events dispatched by dispatchEvent to work as well, though I hadn't tested this.
You cannot affect the state of an element that is display: none by interacting directly with the element, because that element is not rendered. However you can still affect its state by interacting with other elements that fire events that in turn change the state of that element via the DOM.
Some event must be firing or else the second CSS selector would not work.
Can someone explain this?
States and events are completely different. Events do not need to have fired for the state of an element to change, and when an element changes its state it does not necessarily fire any events. Selectors never operate on events; they are always state-based.
In your example, if the input element was already checked via the checked attribute, then the DOM loads with that element in that state to begin with, and no events would be fired, and it would still allow otherElement to match the selector immediately. Furthermore, it would continue to match that selector, until something happens that causes the input to become unchecked (or in CSS selector terms, :not(:checked)).
See my answer to this other question for a little more explanation on states vs events in CSS.
It will have no effect on your layout, as if it didn't exist, but the content is still present in the HTML DOM. The CSS attribute display:none is just that - a CSS attribute, and it does not modify your HTML content.
You can still perform javascript functions on the element.