Accessing all inner elements in Polymer? - javascript

I have:
<k-myelement>
<k-anotherelement></k-anotherelement>
</k-myelement>
When I define the template like this:
<polymer-element name="k-myelement">
<template>
<content select="k-anotherelement" id="anotherelement"></content>
</template>
</polymere-element>
I can access the inner element with this.$['anotherelement']
But with this approach I have to predefine, which inner elements can be used.
What I want is a template technique, that allows me to access all the inner elements.

<content> (insertion points) are for rendering elements in the light DOM at specific locations in the Shadow DOM. Using <content select="k-anotherelement"></content> says "render any <k-anotherelement> elements here. If you want all light DOM nodes to be invited into the rendering party, simply use <content></<content>.
The other issues with your snippet:
The name of the element needs to be defined on <polymer-element>, not as <template name="k-myelement">
To get the list of nodes that pass through a <content>, use content.getDistributedNodes(). You may also want to consider if you even need <content>. Light DOM children nodes can be access with .children and the other accessors. From the Polymer docs:
For a <content>, you can iterate through content.getDistributedNodes() to get the list of nodes distributed at the insertion point.
In Polymer, the best place to call this method is in the attached() callback so you’re guaranteed that the element is in the DOM tree.
Also remember that you can access the light DOM as the element’s normal children (i.e. this.children, or other accessors). The difference with this approach is that it’s the entire set of potentially distributed nodes; not those actually distributed.

Related

Custom Web Elements / Lit Elements - Render child elements only when a parent element is present and way to transfer data among all child elements?

New to Stack Overflow and Lit.
I'm building something using Lit and wanted to know how do I render element/s only if a parent element is present. I am trying to add a login element dependency which will let user to use these elements only if the login element is present.
Example of what should be allowed.
<login-element>
<child-element-button> </child-element-button>
</login-element>
Example of what should not be allowed.
<child-element-button> </child-element-button>
Another problem that I have is a way for the child elements to share data, it can be to and from sibling elements, children elements, grandchildren element and so on. I do not have a solution for data sharing currently and would like to know the possible ways to achieve this.
Is there a way to achieve all of this without making <login-element> the parent element? something like this?
<login-element> </login-element>
<div> ... </div>
<my-custom-button> </my-custom-button>
<p> ... </p>
<my-colors> </my-colors>
<my-custom-footer> </my-custom-footer>
In short, I need users to be able to use custom elements only if <login-element> if present anywhere in the document and the custom elements that are present should be able to communicate between each other. For instance, a <my-colors> element should be able to send active color data to <display-image> element which will render an image with a specific background color received from `.
Currently, I read the child elements of <login-element>, copy the nodes, run loop, delete original nodes and append with those copied nodes. Additionally, in every child elements, I check if <login-element> is present in DOM or not, if present, render, else render a error element. As for passing and receiving data to and from other components, I have not tried anything.
Than you for your time.

GetElementById from within Shadow DOM

I have a custom-element with shadow DOM, which listens to attribute target change.
target is supposed to be the ID of the element which my component is supposed to be attached to.
I've tried using querySelector and getElementById to get the element of the outer DOM, but it always returns null.
console.log(document.getElementById(target));
console.log(document.querySelector('#' + target));
Both of the above return null.
Is there a way to get a reference to the element in the parent document from within shadow DOM?
You just have to call Shadow​Root.
this.shadowRoot.getElementById('target') should work.
Here's an example, the get syntax will bind an object property to a function.
get target() {
return this.shadowRoot.getElementById('target');
}
There are two use cases of shadow DOM as far as I can see:
You control the the shadow DOM solely through your hosting custom element (like in the answer of #Penny Liu). If you want make sure no other script should call and alter the nodes than this is your choice. Pretty sure some banking websites use this method. You give up on flexibility though.
You just want to scope some parts of your code for styling reasons but you like to control it via document.getElementById than you can use <slot>. After all, many libraries rely on the document object and will not work in shadow DOM.
Back to the problem, what you probably did was something like this:
shadowRoot.innerHTML = `...<script>document.getElementById('target')</script>`
// or shadowRoot.appendChild
This is NOT working! And this is not how shadow DOM was anticipated to work either.
Recalling method 2, you SHOULD fill your shadow DOM solely by <slot> tags. Most minimal example:
<!-- Custom Element -->
<scoped-playground>
<style>some scoped styling</style>
<div id="target"></div>
<script>const ☝☝☝☝ = document.getElementById('target')</script>
</scoped-playground>
<!-- Scoped playground has a shadowRoot with a default <slot> -->
...
this.shadowRoot.innerHTML = "<slot>Everything is rendered here</slot>";
...
More advanced <slot> examples can be found at:
https://developers.google.com/web/fundamentals/web-components/shadowdom#composition_slot

how to reference a child element on a cloned element

I am running some tests on a DOM element,
the result of the tests is one of the element descendants.
for example:
<div id="myelement" class="some-class">
<div class="some-child-class"></div>
<div class="some-other-child-class">
<div class="grandchild-class"></div>
<div class="another-grandchild"></div>*
</div>
</div>
let's assume that:
test(document.getElementById('myelement'));
will return the Element marked with asterisk
Now my problem is:
The test procedure is heavy and resource consuming.
I don't want to run it when i don't have to.
And sometimes, I clone an element that has already been tested (meaning - i KNOW the result of the test), but since I am getting an object reference as a result I can't use it to access the relevant child on the cloned element.
Is there any efficient way of somehow "save" the relative path from a parent Element to a specific descendant DOM element and then "apply" it on another element?
So you could assign unique ids to each element and cache the test results in an Object at the Elements id. However, i dont know if this is useful. An example implementation:
var test=function(el){
return this[el.id] || (this[el.id]=advancedtesting(el));
}.bind({});
So you could do:
test(document.getElementById('myelement'));//caches
test(document.getElementById('myelement'));//returns the cached
You could use jQuery for that.
The selectors they use can take the form of 'nth-child' or 'nth-of-type' (see documentation here).
What is does is that targets child element from the position they have relative from where you start from.
In your case if you want to start from your first element and go down to the starred one you can do:
$('#myelement').find('div:nth-child(2) > div:nth-child(2)')
What this does is that it takes #myelement as a base from which you will begin the search, and after that it goes down to the second child element that is a div, and again into this div's second child element.
You could reuse that selector with a different base.

Fastest way to repeatedly clone a set of nodes

I have a curiosity with a plugin I have written for Knockout, called knockout-fast-foreach, namely what is the fastest way to repeatedly clone a set of nodes and inject them into the DOM.
There are two things that need to happen in the cloning, namely copying out the source template nodes, and injecting them back into the DOM.
Now there are some design choices that apply, that include:
The source nodes will be children of a single DOM entity;
The target may have siblings unaffected by the DOM injection i.e. not all the child nodes may change;
The source may be a <template>, <script> or regular HTML DOM node.
So for example:
<template id='src'>ø</template>
<div id='target' data-bind='fastForEach: $data'>
</div>
When one applies the binding with ko.applyBindings([1, 2, 3], document.getElementById('target')) the result will be:
<template id='src'>ø <span data-bind='text: $data'></span></template>
<div id='target' data-bind='fastForEach: $data'>
ø <span data-bind='text: $data'>1</span>
ø <span data-bind='text: $data'>2</span>
ø <span data-bind='text: $data'>3</span>
</div>
While that example is KO-specific, the performance of the DOM manipulation ought to be a relatively universal characteristic.
As you can see from the linked source code above, the way I have come up with so far is to copy the source nodes into an array, then clone + inject them into the target at the desired position.
Is is possible there is a faster way to clone and copy multiple elements (e.g. perhaps using document fragments)?
You are using data-binding. This is in itself going to be slow. The best performance is always going to be to manipulate a string off the dom and then insert it into the dom in one go - element.innerHTML = "your new html". Even better to have it not positioned inline because this slows the rendering of the browser. Itereratively adding to the dom is thrashing the browser renderer. See - http://davidwalsh.name/css-js-animation.

Visually cloning a DOM element

I have a search box somewhere on top of my layout
<input type="text" id="rec_search_input" ...>
I want to clone this element to a second spot, it must look the same and behave the same.
Now I know an ID must be unique, but,.. imagine like a unix symlink, having the same file accessable from different directories.
Is it possible to clone a DOM element and reuse its attached styles + event handlers, ... ?
A proper solution would be to give the .cloneNode() a new ID, and only apply CSS to the element's class rather than its ID. So:
var clone = document.getElementById("rec_search_input").cloneNode();
clone.id = "some other id";
...and then append that elsewhere in the DOM (e.g., via appendChild, insertBefore, etc.). (Side note: Since your element is an input, it can't have children, but if you use this for other things, you can use cloneNode(true) to clone its descendant elements; then spin through them to be sure their id values are unique.)
To some extent, you can "cheat". ID uniqueness isn't enforced by the browser or anything, and CSS will apply to elements with the same ID even if there are more than one. Demo
However, as you are probably aware, JavaScript functions like getElementById will not work predictably if you have duplicate IDs.

Categories