Consider the w3schools example of the :target selector:
<!DOCTYPE html>
<html>
<head>
<style>
:target {
border: 2px solid #D4D4D4;
background-color: #e5eecc;
}
</style>
</head>
<body>
<h1>This is a heading</h1>
<p>Jump to New content 1</p>
<p>Jump to New content 2</p>
<p>Click on the links above and the :target selector highlight the current active HTML anchor.</p>
<p id="news1"><b>New content 1...</b></p>
<p id="news2"><b>New content 2...</b></p>
<p><b>Note:</b> Internet Explorer 8 and earlier versions do not support the :target selector.</p>
</body>
</html>
When you click on the anchor referencing another element it styles thep with the corresponding id.
I want the effect of :target but on hover instead. how is this done?
how do you style the thing that href points to in the page on hover?
if this is not possible. what is the best-performing javascript solution?
Because CSS selectors can only traverse from an earlier element to a later sibling, descendant or descendant of a sibling (and cannot select parent, or previous-sibling, elements), this cannot be done with CSS. As hovering the <a> to style the later :target-ed elements would first require traversing to the parent from the hovered-<a> element.
To do this with JavaScript, then, I'd suggest:
// a named function to toggle the highlighting of the
// targeted element:
function highlightTarget(event) {
// the 'event' is passed automagically from the
// addEventListener() method; as is the 'this'
// which is the element to which the event-handler
// (this function) was bound:
// using getAttribute() to get the value of the attribute,
// instead of 'this.href' which would get the absolute URL,
// replacing the leading '#' character with an empty string:
var id = this.getAttribute('href').replace(/^#/, ''),
// getting the element with that id:
target = document.getElementById(id);
switch (event.type) {
// if this is the mouseenter event we add the 'highlight'
// class-name:
case 'mouseenter':
target.classList.add('highlight');
break;
// on 'mouseleave' we remove the class-name:
case 'mouseleave':
target.classList.remove('highlight');
break;
}
}
// iterating over the NodeList returned by
// document.getElementsByTagName(), using
// Array.prototype.forEach():
Array.prototype.forEach.call(document.getElementsByTagName('a'), function(a) {
// if the href attribute (not property) begins with a '#':
if (a.getAttribute('href').indexOf('#') === 0) {
// we bind the highlightTarget function to handle
// both the 'mouseenter' and 'mouseleave' events:
a.addEventListener('mouseenter', highlightTarget);
a.addEventListener('mouseleave', highlightTarget);
}
});
.highlight {
background-color: red;
}
<h1>This is a heading</h1>
<p>Jump to New content 1
</p>
<p>Jump to New content 2
</p>
<p>Click on the links above and the :target selector highlight the current active HTML anchor.</p>
<p id="news1"><b>New content 1...</b>
</p>
<p id="news2"><b>New content 2...</b>
</p>
<p><b>Note:</b> Internet Explorer 8 and earlier versions do not support the :target selector.</p>
It is worth noting, though, that the CSS Selectors Module, Level 4, has a proposed solution, the reference-combinator, to address this:
The following example highlights an element when its is focused or hovered-over:
label:matches(:hover, :focus) /for/ input, /* association by "for" attribute */
label:matches(:hover, :focus):not([for]) input { /* association by containment */
box-shadow: yellow 0 0 10px;
}
Which suggests that the correct syntax (which, currently of course, does not work) may be:
a:matches(:hover) /href/ p {
background-color: red;
}
References:
CSS:
Reference combinator (E:matches(:hover) /href/ p).
JavaScript:
Array.prototype.forEach().
Element.getAttribute().
EventTarget.addEventListener().
Function.prototype.call().
Guide to JavaScript Regular Expressions.
String.prototype.indexOf().
String.prototype.replace().
switch () {...} operator.
For Info:
In CSS if link is ahead and adjacent to the target or target's parent, then you could do something similar:
[href="#news1"]:hover ~#news1,
[href="#news2"]:hover ~#news2{
border: 2px solid #D4D4D4;
background-color: #e5eecc;
}
<h1>This is a heading</h1>
Jump to New content 1
Jump to New content 2
<p>hover link and see target element highlight via <code>[href="#target] ~#target </code></p>
<p id="news1"><b>New content 1...</b></p>
<p id="news2"><b>New content 2...</b></p>
<p><b>Note:</b> links must be ahead and adjacent to target or parents target in order to work.</p>
To go further and understand ,See: http://www.w3.org/TR/css3-selectors/#attribute-representation
and notice those too : http://www.w3.org/TR/css3-selectors/#adjacent-sibling-combinators & http://www.w3.org/TR/css3-selectors/#general-sibling-combinators
Related
This question already has answers here:
What is the ::content/::slotted pseudo-element and how does it work?
(2 answers)
Closed 5 months ago.
The community reviewed whether to reopen this question 5 months ago and left it closed:
Original close reason(s) were not resolved
The CSS ::slotted selector selects children of the <slot> element.
However, when trying to select grandchildren like with ::slotted(*), ::slotted(*) *, or ::slotted(* *), the selector doesn't seem to take effect.
class MyElement extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'})
shadowRoot.innerHTML = `
<style>
::slotted(*) {
display: block;
border: solid blue 1px;
padding: 3px;
}
::slotted(*) span {
display: block;
border: solid red 1px;
padding: 3px;
}
::slotted(* span) {
display: block;
border: solid green 1px;
padding: 3px;
}
</style>
<slot></slot>
`;
}
}
customElements.define('my-element', MyElement);
<my-element>
<p>
<span>Test</span>
</p>
</my-element>
Note how the span doesn't get the border.
Is this expected behavior? I wasn't able to find concrete documentation for this.
If yes, is there a way to work around this?
styling ::slotted elements in shadowDOM
TL;DR
::slotted Specs: https://drafts.csswg.org/css-scoping/#slotted-pseudo
slotted content remains in light DOM, is reflected to a <slot> in shadow DOM
::slotted(x) targets the lightDOM outer-Element (aka 'skin'), NOT the SLOT in shadowDOM
::slotted(x) takes basic selectors
Inheritable styles trickle into shadowDOM
https://lamplightdev.com/blog/2019/03/26/why-is-my-web-component-inheriting-styles/
For the latest WHATWG discussion on SLOT and related topics, see
https://github.com/whatwg/html/issues/6051#issuecomment-816971072
Participants: rniwa (Apple) , annvk (Mozilla), dominic (Google)
https://github.com/WICG/webcomponents/issues/934#issuecomment-906063140
Interesting reads:
A history of the HTML <slot> element by Jan Miksovsky
Summary of positions on contentious bits of Shadow DOM โ Web Components F2F on 2015-04-24
background
Yes, ::slotted() not styling nested elements is expected behavior.
The term slotted is counterintuitive,
it implies element lightDOM is moved to shadowDOM
slotted lightDOM is NOT moved, it remains.. hidden.. in lightDOM
the content (IF slotted) is reflected to a <slot></slot>
Or from Google Developer Documentation
๐พ๐ค๐ฃ๐๐๐ฅ๐ฉ๐ช๐๐ก๐ก๐ฎ, ๐๐๐จ๐ฉ๐ง๐๐๐ช๐ฉ๐๐ ๐ฃ๐ค๐๐๐จ ๐๐๐ฃ ๐จ๐๐๐ข ๐ ๐๐๐ฉ ๐๐๐ฏ๐๐ง๐ง๐.
๐๐ก๐ค๐ฉ๐จ ๐๐ค๐ฃ'๐ฉ ๐ฅ๐๐ฎ๐จ๐๐๐๐ก๐ก๐ฎ ๐ข๐ค๐ซ๐ ๐ฟ๐๐; ๐ฉ๐๐๐ฎ ๐ง๐๐ฃ๐๐๐ง ๐๐ฉ ๐๐ฉ ๐๐ฃ๐ค๐ฉ๐๐๐ง ๐ก๐ค๐๐๐ฉ๐๐ค๐ฃ ๐๐ฃ๐จ๐๐๐ ๐ฉ๐๐ ๐จ๐๐๐๐ค๐ฌ ๐ฟ๐๐.
I use the term reflected instead of render because render implies you can access it in shadowDOM.
You can not, because slotted content isn't in shadowDOM... only reflected from lightDOM.
Why :slotted has limited functionality
More advanced shadowDOM styling was tried.
WebComponents version 0 (v0) had <content> and ::content; but it was removed from the spec:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/content
The main takeway from the W3C standards discussions
(#hayatoito (Google team) here and here) is:
So in V1 we have :slotted: https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted
Addition #1 : Performance if ::slotted allowed for complex selectors
From Mozilla developer Emilio:
source: https://github.com/w3c/webcomponents/issues/889
The performance issue is that it increments the amount of subtrees in
which every node needs to go look for rules that affect to them.
Right now the logic goes like: if you're slotted, traverse your slots
and collect rules in their shadow trees as needed. This is the code
This is nice because the complexity of styling the element
depends directly on the complexity of the shadow trees that you're
building, and it only affects slotted nodes.
If you want to allow combinators past slotted then every node would
need to look at its ancestor and prev-sibling chain and look at which
ones of them are slotted, then do that process for all their slots.
Then, on top, you also need to change the general selector-matching
code so that selectors that do not contain slotted selectors don't
match if you're not in the right shadow tree.
That's a cost that you pay for all elements, regardless of whether you
use Shadow DOM or ::slotted, and is probably just not going to fly.
So due to performance issues
:slotted( S ) got limited CSS selector functionality:
โบ it only takes simple selectors for S. --> Basically anything with a space won't work
โบ it only targets lightDOM 'skin'. --> In other words, only the first level
<my-element>
<h1>Hello World</h1>
<p class=foo>
<span>....</span>
</p>
<p class=bar>
<span>....</span>
</p>
</my-element>
::slotted(h1) and ::slotted(p) works
::slotted(.foo) works
::slotted(span) (or anything deeper) will not work (not a 'skin' element)
Note: ::slotted([Simple Selector]) confirms to Specificity rules,
but (being simple) does not add weight to lightDOM skin selectors, so never gets higher Specificity.
You might need !important in some (rare) use cases.
<style>
::slotted(H1) {
color: blue !important;
}
<style>
Styling slotted content
Also see: Applying more in depth selection to the :host CSS pseudo class
#1 - style lightDOM
The <span> is hidden in lightDOM, any changes made there will continue to reflect to its slotted representation.
That means you can apply any styling you want with CSS in the main DOM
(or a parent shadowDOM container if you wrapped <my-element> in one)
<style>
my-element span {
.. any CSS you want
}
<style>
#2 - (workaround) move lightDOM to shadowDOM
If you move lightDOM to shadowDOM with: this.shadowRoot.append(...this.childNodes)
you can do all styling you want in a shadowDOM <style> tag.
Note: You can not use <slot></slot> and :slotted() anymore now.
<slot>s only works with content reflected from lightDOM.
For an example where an element wraps itself in an extra shadowDOM layer,
so no CSS bleeds out, and <slot>s can be used, see:
https://jsfiddle.net/WebComponents/5w3o2q4t/?slotmeister
#3 - ::part (shadow Parts)
It is a different/powerful way of styling shadowDOM content:
Apple finally implemented shadowParts in Safari 13.1, March 2020
see:
https://meowni.ca/posts/part-theme-explainer/
https://css-tricks.com/styling-in-the-shadow-dom-with-css-shadow-parts/
https://dev.to/webpadawan/css-shadow-parts-are-coming-mi5
https://caniuse.com/mdn-html_global_attributes_exportparts
Note! ::part styles shadowDOM,
<slot></slot> content remains in lightDOM!
references
be aware: might contain v0 documentation!
https://css-tricks.com/encapsulating-style-and-structure-with-shadow-dom/
https://developers.google.com/web/fundamentals/web-components/shadowdom?hl=en#composition_slot
https://polymer-library.polymer-project.org/2.0/docs/devguide/style-shadow-dom#style-your-elements
https://github.com/w3c/webcomponents/issues/331
https://github.com/w3c/webcomponents/issues/745
https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/slotchange_event
::part() - https://developer.mozilla.org/en-US/docs/Web/CSS/::part
Example: Using slots as a router
Change the slot-name on buttonclick and reflect content from lightDOM:
<template id=MY-ELEMENT>
<style>
::slotted([slot="Awesome"]){
background:lightgreen
}
</style>
<slot><!-- all unslotted content goes here --></slot>
<slot id=answer name=unanswered></slot>
</template>
<style>/* style all IMGs in lightDOM */
img { max-height: 165px;border:3px dashed green }
img:hover{ border-color:red }
</style>
<my-element><!-- content below is: lightDOM! -->
SLOTs are: <button>Cool</button> <button>Awesome</button> <button>Great</button>
<span slot=unanswered>?</span>
<div slot=Cool> <img src="https://i.imgur.com/VUOujQT.jpg"></div>
<span slot=Awesome><b>SUPER!</b></span>
<div slot=Awesome><img src="https://i.imgur.com/y95Jq5x.jpg"></div>
<div slot=Great> <img src="https://i.imgur.com/gUFZNQH.jpg"></div>
</my-element>
<script>
customElements.define('my-element', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode:'open'})
.append(document.getElementById(this.nodeName).content.cloneNode(true));
this.onclick = (evt) => {
const label = evt.composedPath()[0].innerText; // Cool,Awesome,Great
this.shadowRoot.getElementById("answer").name = label;
}
}
});
</script>
I have div with its own style. I embedded this div on other website.
<div id="scoped-div">
<style>
label {
color: green;
}
</style>
<label> Scoped div </label>
</div>
But I face problem, my div style is overridden by website style. I don't want to use iframe. Except for the use of iframe is there any other way to protect my div style by external style changes?
Your request is exactly what Shadow DOM makes possible:
attach a Shadow DOM to the element you want to protect (here:
#scope-div),
put the HTML code you want to protect in the Shadow DOM,
clone it from a <template> element to get it easy (optional).
That's it!
var div = document.querySelector( "#scoped-div" )
var template = document.querySelector( "template" )
var sh
if ( 'attachShadow' in div )
sh = div.attachShadow( { mode: "closed" } ) //Shadow DOM v1
else
sh = div.createShadowRoot() //Shadow DOM v0 fallback
sh.appendChild( template.content.cloneNode( true ) )
<template>
<style>
label {
color: green;
}
</style>
<label> Scoped div </label>
</template>
<div id="scoped-div">
</div>
There is no way to fully protect your styles. But you can try the following:
Try to specify your elements selectors as specific as possible (e.g. with attributes and IDs)
Use inline styles
Use !important (but be careful with a broad use of importants)
I have this html code here:
<div default_name="RandomName1">
<div name="RandomName1">RandomName1</div>
<div name="RandomName2">RandomName2</div>
<div name="RandomName3">RandomName3</div>
</div>
The property default_name on parent div changes from time to time. I would like to set the child div which has name matching default_name to background-color:red.
Like:
<style>
div > div[name=default_name_of_parent] { background-color: red }
</style>
I have no control over what the name values are, users set it. Is this possible via style sheet?
Thanks
This can be done, if you make a rule containing a selector for each possible โcombinationโ, like so:
div[default_name=RandomName1] > div[name=RandomName1],
div[default_name=RandomName2] > div[name=RandomName2],
div[default_name=RandomName3] > div[name=RandomName3]
{ background-color: red }
http://jsfiddle.net/wc5whfwa/
But j08691 is totally right with their comment โ this should be avoided at all cost if possible, data- attributes would be the way to go.
How to change the color only from text except everything is between tags ?
Sample text:
<b>A7</b> <b>D</b>
this is a test
<b>A7+</b> <b>G9</b>
this is a test
Assuming that all of that text is wrapped in a parent element (I've used <div>, but almost any other element would suffice), as such:
<div>
<b>A7</b>
<b>D</b>
this is a test
<b>A7+</b>
<b>G9</b>
this is a test
</div>
Then you can't change "all the text except the <b> tags", because CSS won't allow you to style the text without affecting the colour of the the <b> elements, you can, however, style the div and then 'unstyle' the b elements:
div {
color: #f00;
}
div b {
color: #000;
}
JS Fiddle demo.
To do this with jQuery (and, honestly, from the information you've posted jQuery seems unnecessary), you'd have to create wrapping elements for each of the strings of characters that are not wrapped in b elements and then directly style, or add a class to, those elements:
$('body').contents().filter(function(){
return this.nodeType === 3 && this.nodeValue.trim().length > 0;
}).wrap('<span />').parent().css('color','red');
JS Fiddle demo.
References:
contents().
filter().
parent().
wrap().
Try:
body{color:red;}
b{color:black;}
Fiddle here.
You could use jQuery like this:
$('body').css('color', '#FFCCFF');
$('b').css('color', '#000000');
But if you can do it in CSS it would be better:
body {
color: #FFCCFF;
}
b {
#000000;
}
Since you tagged this as jquery, I just provided a solution for this with jquery, You may wrap the html which was written by you in a paragraph tag like below. And then you have to use the .addClass function of Jquery to set different classes with different colours for that both paragraph and bold tag.
HTML
<p><b>A7</b><b>D</b>
this is a test
<b>A7+</b><b>G9</b>
this is a test</p>
CSS
.Paragraph{
color:red;
}
.boldtext{
color:black;
}
JQUERY
$('p').addClass('Paragraph');
$('p > b').addClass("boldtext");
DEMONSTRATION
This is my HTML:
<p class="define"><a class="term" href="#jump" name="jump">jump</a> - Some description</p>
When the browser jumps to #jump I want to highlight the .define class. If I do;
a.term:target { background-color: #ffa; -webkit-transition: all 1s linear; }
I of course only highlight the a. How do I modify this to complete p? I tried a couple of variants like the one below, but none of them work.
a.term:target .define { stuff here }
a.term:target p.define { stuff here }
a.term:target p { stuff here }
jsFiddle: http://jsfiddle.net/vVPPy/
You can't modify the parent of an element using css. You will have to use a javascript alternative.
You will not be able to determine where the user is on the page using CSS. This can be accomplished with JavaScript - If you're not trying to reinvent the wheel, I'd recommend using Bootstrap's ScrollSpy.
Your <p> tag isn't the target of anything. If it were:
<p class="define" id="something">
<a class="term" href="#something" name="jump">jump</a> - blah
</p>
You could style it like so:
a.term:target { background-color: #ffa; }
but that has nothing to do with the <a> actually being clicked on. You'll need to use an onclick handler for that, ideally adding a class to the target and styling based on that class.