:root CSS selector inside Shadow DOM [duplicate] - javascript

I need a selector for usage in css inside a shadow root, which selects all children (but not grand children) of the shadow root, no matter what tag they are and without giving them an ID.
In the example below, SPAN,A,P and DIV should get a red border, but SPAN IN DIV not.
<my-element>
#shadow-root
<span>SPAN</span>
<a>A</a>
<p>P</p>
<div>
DIV
<span>SPAN IN DIV</span>
</div>
<style>
:root>*{border:1px red solid;}
</style>
</my-element>
I hoped, the :root-Selector would do the job inside of a shadow dom, but thats not the case.
It would also be a possible solution if someone shows how to set an ID on the shadow root.
Update:
Tried using #shadow-root > * as selector:
seems not to work. Probably it is just google chrome developer tools visualizing the shadow root element like that.

Use this selector: :host > *
The :host selector is described in https://drafts.csswg.org/css-scoping/#host-selector
document.querySelector( 'my-element' )
.attachShadow( { mode: 'open' } )
.innerHTML = `
<span>SPAN</span>
<a>A</a>
<p>P</p>
<div>
DIV
<span>SPAN IN DIV</span>
</div>
<style>
:host>*{border:1px red solid;}
</style>`
<my-element>
</my-element>
:host may also hold a compound selector, which must be places in brackets. E.g. :host([foo=bar]) selects a host element which has attribute foo set to bar.

we can use the shadow() method from cypress. you can use the get('selector before the shadow-root') method then shadow() method and use the find('locator') till your control/elements and at last you invoke the actual method e.g. click() or type() or select('index') on that control/element. Also you can use {force:true} aswell.
cy.get("mc-select[name='taxTypeCodes']").shadow().find('div.mc-component-template ').find('div').find('label.mc-input__container').find('div.mc-input__field').find('select').select('IN3',{force:true})
For more details please refer-cypress-shadow-dom

Related

How can I select text in an element that isn't inside another element? [duplicate]

I have the following HTML markup:
<h1>
<div class="sponsor">
<span>Hello</span>
</div>
World
</h1>
When I use the CSS selector h1 I get Hello World.
I can't unfortunately change the markup and I have to use only CSS selectors because I work with the system that aggregates RSS feeds.
Is there any CSS selector which I can take only the text node? Specifically the World in this example?
The current state of CSS can't do this, check this link: W3C
The problem here is that the content you write to the screen doesn't show up in the DOM :P.
Also ::outside doesn't seem to work yet (at least for me in Safari 6.0.3) or it simply doesn't generate the desired result yet.
Check my fiddle and then check the DOM source: JSfiddle
Finally there are attribute selectors a { content: attr(href);}, making CSS able to read DOM-node attributes. There doesn't seem to be a innerHTML equivalent of this yet. It would be great tho if that was possible, whereas you might be able to manipulate the inner markup of a tag.
Bit of a workaround:
h1 {
color: red;
}
h1 * {
color: lime;
}
<h1>
<div class="sponsor">
<span>Hello</span>
</div>
World
</h1>
This is almost the opposite of a question I asked last week: Is it possible to select the very first element within a container that's otherwise pure text without using classes or identifiers in pure CSS?
The short answer is no. "World" in this example isn't an element of its own - therefore there isn't a way to select it.
What you would have to do here is style the h1 then override that styling with div.sponsor. For instance, if you wanted "World" here to have a black background with white text you woud use something similar to:
h1 {
background:black;
color:white;
}
h1 div.sponsor {
background:white;
color:black;
}
Unfortunately, however, this wouldn't work if you were only wanting the word "World" styled and your markup had more than just that within <div>Hello</div> World Foo, for instance.
I don't believe it would be possible with pure CSS to style just "World" in this situation.
I also met same problem, where I can't touch the markup and have no control with js.
I needed to hide a text nodes in a div element, but the element to remain visible.
So here is my solution:
markup:
<div id="settings_signout_and_help">
<a id="ctl00_btnHelpDocs" class="ico icoHelp" href="http://" Help Guide</a>
Signed in as: <a id="ctl00_lUsr" href="Profile.aspx">some</a>
Home
Sign out
</div>
css:
#settings_signout_and_help {
font-size: 1px !important;
}
#settings_signout_and_help a {
font-size: 13px !important;
}
Hope this helps guys!
I had a similar problem where I had to remove the "World" text from html generated by a C# function.
I set the font-size to 0 on the 'h1' element and then applied my css to div class. Basically hiding the extra text, but keeping content in the div.
I don't know how to do it with just CSS, but...
Using JQuery, you could select all the elements inside except the stuff inside its child element
$("h1:not(h1 > div)").css()
and put whatever CSS effect you want inside there.

Is it possible to stop inheriting CSS within shadowDom from HTML tag?

Im injecting some Javascript that creates an isolated div located at the top of the body. Within this div there is a shadowDom element. The reason I went with shadowDom is because I thought it stoped CSS from bleeding in to all the divs within the shadowDom. But I can clearly see that it is inheriting style from the tag(font-size: 62.5%;). This is causing my text to be smaller. I can override this with adding font-size: 100% !Important but even though it crosses it out in the inspector tools it does not actually change. The only way I can get it to work is by unchecking the box in the CSS portion.
Please Help
Thanks,
Dev Joe
HTML Shadow Dom IMAGE
CSS Checked IMAGE
CSS Unchecked IMAGE
You should not use a relative font size (like 100%) because it applies to inherited size... so this will have no effect.
Insead, you should define a rule to the :host CSS peudo-class:
:host {
font-size: initial ;
}
NB: You'll need to add !important only if the font-size defined in the container (the main document) applies to the host element directly.
NB #2: You can use all: initial instead but you cannot combine it with !important.
host.attachShadow( { mode: 'open' } )
.innerHTML = `
<style>
:host { all: initial }
</style>
Inside Shadow Root <br>
<div>Div in Shadow DOM</div>
<slot></slot>
`
body { font-size : 62.5% ; color: red }
Small Font
<div>Div in main Document</div>
<div id=host>Light DOM</div>
No need for shadow dom, just use the all attribute to disable the inheritance.
#myElement {
all: initial;
}

ShadyCSS polyfill not properly handling CSS in Edge

I am building a widget for third-party websites, using shadow DOM to prevent their CSS from interfering with ours. I am using the ShadyDOM and ShadyCSS polyfills to make it work in Edge and IE, but it is not transforming the CSS for the shadow DOM as I would expect.
Example:
<!DOCTYPE html>
<html>
<head>
<title>Shadow DOM test</title>
</head>
<body>
<div id="container">container is here</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.3.0/webcomponents-bundle.js"></script>
<script>
const shadow = document.getElementById("container").attachShadow({ mode: "open" });
const style = document.createElement("style");
style.innerHTML = `
:host .stuff {
background: #ff00ff;
}
`;
shadow.appendChild(style);
const div = document.createElement("div");
div.classList.add("stuff");
div.innerHTML = "stuff inside shadow dom";
shadow.appendChild(div);
</script>
</body>
</html>
In Chrome (which supports shadow DOM natively), the stuff div has a pink background, as I would expect. But in Edge (which does not support shadow DOM natively), I see the "stuff inside shadow dom" text (meaning my script ran and the ShadyDOM functions worked), but I don't see the pink background.
Why is this happening? I am attaching a shadow root to a plain old div, instead of using custom elements as the example in the ShadyCSS README does, but does that matter? If so, how can I make this work? I am working on a big, existing app, and not wanting to make too many changes at once, so I would strongly prefer to use the standard HTML elements I am already using (divs, buttons, etc.) instead of coming up with my own elements or templates, although I would be willing to consider templates and/or custom elements if it can be done easily, without having to make a lot of big changes.
With ShadyCSS
:host CSS pseudo-element is not known in Edge.
To make it work, you should use ShadyCSS.prepareTemplate() that will replace :host by the name of the custom element and define the style as a global style that will apply to all the page.
Remember that there's no Shadow DOM in Edge: there's no boundaries/scope for CSS with a fake/polyfilled Shadow DOM.
In your case you could use ShadyCSS.prepareTemplate( yourTemplate, 'div' ) as in the example below:
ShadyCSS.prepareTemplate( tpl, 'div' )
container.attachShadow( { mode: "open" } )
.appendChild( tpl.content.cloneNode(true) )
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.3.0/webcomponents-bundle.js"></script>
<template id=tpl>
<style>
:host .stuff {
background: #ff00ff;
}
</style>
<div class=stuff>stuff inside shadow dom</div>
</template>
<div id=container>container is here</div>
Note: since the polyfill will replace :host by div and add it as a global style, you could observe some side effects if you have another HTML code part that matches div .stuff.
Without ShadyCSS
ShadyCSS was designed for Custom Elements, but not really for standard elements. However, you should get inspiration from the polyfill and create explicitely the styles properties for fake (polyfilled) Shadow DOM. In your case replace :host with div#containter:
container.attachShadow( { mode: "open" } )
.appendChild( tpl.content.cloneNode(true) )
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.3.0/webcomponents-bundle.js"></script>
<template id=tpl>
<style>
div#container .stuff {
background: #ff00ff;
}
:host .stuff {
background: #ff00ff;
}
</style>
<div class=stuff>stuff inside shadow dom</div>
</template>
<div id=container>container is here</div>

Keep styling when adding dynamic elements javascript

So I am trying to add some images dynamically that are uploaded from pc to a gallery div. But when I add them with their class names, their styling is the default and not their class styling.
I know that I can add the styling dynamically when adding the image but the styling of the class is very large. Is there any possible way we can add the styling of the class but not when we add the image?
function uploadImage(){
var appendedText="<div class='p-item grid-sizer'><a class='venobox' href='images/porfolio-16-300x500.jpg'><img src='images/porfolio-16-300x500.jpg' alt=''/></a></div>";
var x=document.getElementsByClassName("p-grid-isotope");
x[0].appendChild(appendedText);
}
<section class="main-section" >
<div class="p-grid-isotope" >
<div class="p-item grid-sizer" ></div>
</div>
</section>
<button type="submit" id="upload-img" class="following-button" style="color: white; margin-top: 30px; margin-left: 18%" onclick="uploadImage()"><span class="icons i1"><i class="fa fa-upload" aria-hidden="true"></i></span>Upload to account</button>
You can take advantage of the CSS Combinator and keep the styles in the CSS files only.
Descendant combinator ' '
.p-grid-isotope img {
...
}
or use Child combinator '>' if the img is the direct child.
For starters, in the posted example, you're trying to append a string to a DOM node. If you're going to use appendChild, you need to append a DOM node.
You can use insertAdjacentHTML to append the string of HTML:
x[0].insertAdjacentHTML('afterend', appendedText);
As for your CSS, you need to include the relevant part of the stylesheet so we can see if there's a problem with it. As mentioned by others, CSS styles should be applied even on dynamically inserted elements.
My guess is something is wrong with your markup in appendedText, and it's not matching against the desired CSS selector.

:target css selector not working in polymer

I'm using Polymer and I noticed that the :target css selector doesn't work.
For example
<polymer-element name="my-element" noscript>
<template>
<style>
:target {
border: 2px solid red;
}
</style>
<div id="test">This is a :target test</div>
</template>
</polymer-element>
Click me
<my-element></my-element>
DEMO
Any suggestions how I can fix this ?
I must admit: I'm not very familar with shadow DOM and absolutely not familar with Polymer but I'd like to tell you my view on this because your intention looks somewhat strange to me and this is too long for a comment.
Short
You can't use the pseudo selector :target within a shadow host.
Long
Unfortunately I was not able to find clear evidences in these resources
http://www.w3.org/TR/shadow-dom/
http://dev.w3.org/csswg/css-scoping/
but some hints...
The goal of Web Components was to give us the ability to build individual and isolated components that can be used in a document without caring of their inner function or style.
If a component could directly reach the "outside" document or if the outside document could reach any shadow hosts element directly, this would completely break the intention of Web Components.
Imagine what would happen if you insert two instances of your <my-element>. Both contain the same ID, which one should be targeted?
Of course it's possible to reach the shadow document, or the containing document from within the shadow document, but only through ::shadow or :host respectively.
To me its logical that the browser can't select elements using a mere :target selector since the target is the matter of the document (it's URL is targeted to some ID) not of any shadow DOM. It's also not possible to reach a shadow tree node with document.getElementById() from within the container document.
The CSS scoping spec which also adresses the Shadow DOM concepts states:
Why is the shadow host so weird?
The shadow host lives outside the shadow tree, and its markup is in
control of the page author, not the component author.
It would not be very good if a component used a particular class name
internally in a shadow tree, and the page author using the component
accidentally also used the the same class name and put it on the host
element. Such a situation would result in accidental styling that is
impossible for the component author to predict, and confusing for the
page author to debug.
(3.1.1. Host Elements in a Shadow Tree)
I'd say this is another evidence: the shadow host (viewed from outside) itself will keep the active (focus) state while handling the focus inside its tree.
To maintain encapsulation, the value of the Document object's focus
API property activeElement must be adjusted. To prevent loss of
information when adjusting this value, each shadow root must also have
an activeElement property to store the value of the focused element in
the shadow tree.
(6.3 Active Element)
One possible solution to your problem
If your intention was to highlight only the div, when your shadow element is :targeted this might be the correct style within your shadow document:
<polymer-element name="my-element" constructor="" attributes="">
<template>
<style>
:host(:target) #inner {
color: #0c0;
}
</style>
<content>Hello World!</content>
<div id="inner">This is a :target test</div>
...
It will highlight the <div> with green text, when your shadow element <my-element id="outer"></my-element> is targeted by #outer.
If this was not your intention and you really wanted to be able to target #inner from outside, I'd say this is not possible (see the "longer" part ;).
I don't think it's a good idea to link to elements inside shadow dom, because you may have multiple instances of the outer element in same page so you'll get multiple elements with same id.
However when you request a url with #elementId the browser will only look in light dom for the according element.
If you still need to style shadow dom elements you could simulate :target selector:
<polymer-element name="my-element" constructor="" attributes="">
<template>
<style>
#inner[target] {
border: 2px solid red;
}
</style>
<content>Hello World!</content>
<div id="inner" target?="{{innerTargetted}}">This is a :target test</div>
</template>
<script>
Polymer('my-element', {
ready: function() {
$(window).on('hashchange', function() {
this.innerTargetted = window.location.hash == '#inner';
}.bind(this));
}
});
</script>
</polymer-element>
Demo.

Categories