jQuery selection in polymer webcomponent - javascript

Well, out of the box, jQuery does not have support for selecting nodes inside webcomponent(s). (probably because document.querySelector() does not work for shadow DOM (nor it should, by definition)).
Our previous codebase was somewhat dependent on jQuery and many of the devs do not want to let go of the simplicity of $(...) selection. So, I wrapped up this quick and dirty trick.
window.$$ = function (that, selector) {
return $(that.shadowRoot.querySelectorAll(selector));
}
Usage (inside a lifetime callback or whenever the host node can be accessed):
jqel = $$(this, '.myClass'); // this has reference to the host
The question is, is there a better way to go about this?

i have created a jquery-polymer plugin that has a lot of functions that may help you dealing with polymer shadow dom
https://github.com/digital-flowers/jquery-polymer
to select any element inside a polymer element lets say
<my-button id='button1'></my-button>
first you need to get the button shadow root using
$("#button1").getShadowRoot()
or
$("#button1").shadow()
this will return the button shadow root as jquery object then you can select anything inside it for example
$("#button1").shadow().find("ul > li:first")
cheers ;)

As far as I know Jquery permits passing context as parameter JqueryContext, so the proper way would be:
$('selector',context)
As an example:
var component1 = document.querySelector('qr-code');
// Find some img inside qr-code component
var img1 = $('img',component1)

Related

Is adding / removing from classlist "ok" in Angular 2?

I have a directive that enables drag and drop for elements. While I am dragging I am giving the dragged element and the elements I am dragging over some classes. Right now I am doing something like this
onDragStart(event: DragEvent) {
event.dataTransfer.setData("text/plain", this.article.id.toString());
this.el.classList.add(this.draggedItemClass);
}
this.el is the native element that I am dragging. I.e. I am writing a css class from my Component using JavaScript. I found similar code in some ng library but it feels like I am doing something wrong and should not write css-classes with JavaScript and rather should do this via my template. So my question is is adding / removing from classlist "ok" in Angular 2 or should I go via the template in some way (probably using my data as a basis)?
From a directive, you can always use #HostAttribute binding. And that is the best practice for sure.
Example:
#HostBinding('class.someClass') someField: boolean = false;
so in that onDragStart, you will just do someField = true
This is cleanest and best soulution.
P.S. Your solution is not that bad because you let Angular to give you access to the element which is good, and you are not selecting it manually from the document, but anyway, the above mentioned solution is way better

JavaScript within Shadow DOM best practices

I'm having trouble getting JavaScript to run properly within Shadow DOM elements I'm defining. Given the following code:
<template id='testTemplate'>
<div id='test'>Click to say hi</div>
<script>
var elem = document.querySelector('#test');
elem.addEventListener('click', function(e) {
alert("Hi there");
});
</script>
</template>
<div id="testElement">Host text</div>
<script>
var shadow = document.querySelector('#testElement').createShadowRoot();
var template = document.querySelector('#testTemplate');
shadow.appendChild(template.content.cloneNode(true));
</script>
document.querySelector is returning null. If I wrap it in document.onload it no longer throws the error, but clicking the div doesn't launch the alert, either.
Is document.onload the proper way to handle when my code runs in this case?
Is this the proper way to embed javascript for shadow dom elements?
Shadow DOM tree
You must bind your eventHandler inside the template tag to #testElement:
var elem = document.querySelector('#testElement');
Meaning to the original element / ShadowHost. That is, because Events from ShadowElements appear as if they originated by the ShadowHost.
Javascript is actually not scoped inside of ShadowDOM-Trees. See for example this blog entry, which covers exactly that topic:
Remember when I spent all of that time explaining how Shadow DOM CSS was encapsulated and protected from the parent document and how awesome that all was? You might also think that JavaScript works the same way—I did at first—but that’s actually not the case. [...]
https://robdodson.me/shadow-dom-javascript/
As an explanation for rearranging the events to the ShadowHost the author writes:
This is because events coming from shadow nodes have to be retargeted otherwise they would break encapsulation. If the event target continued to point at #shadow-text then anyone could dig around inside of our Shadow DOM and start messing things up.
https://robdodson.me/shadow-dom-javascript/
I suppose it is a good idea to read other articles of this blog too, as it seems to cover the topic pretty well.
Custom elements
With custom HTML elements you have the ability to use Javascript inside of custom HTML elements, see for example this answer on Stackoverflow.
Basically you must create a complete new HTML element. A tutorial is available at html5rocks. I think this is how for example the Polymer project provides its custom events.

adding element with ng-click using angular.element

I am trying to add a couple of custom buttons to the right part of the header of fullcalendar (http://arshaw.com/fullcalendar/docs/).
I am using angular.element to add the buttons like this:
var calendarHeaderRight = angular.element(".fc-header-right");
if (calendarHeaderRight)
calendarHeaderRight.html('<div>CalendarList </div>');
The buttons do get appended to the calendar and are displayed. The problem is that nothing is happening when I click the buttons.
So the question is, how do I append elements which can be clicked ?
thanks
Thomas
var element = $compile(angular.element('<div>CalendarList </div>'))(scope);
then you can wrapped this element in your div. Here is angular document about compile.
: ) enjoy it.
var html = angular.element('<div>CalendarList </div>')
var compiled = $compile(html)
calendarHeaderRight.html(html)
compiled($scope)
View compilation in Angular basically traverses the entire DOM tree of whatever node you give it, creating a single function, that when called will execute every linking function from every directive it finds in that DOM tree, with the appropriate scope, element, attributes and (optionally) controllers.
Pretty much the same thing Tyler did just broken down.

Possible to live create an dom object with plain JS?

I have this object I need to create with JS and later on I need to do an on hover do something statement...And because this div is created clientside later, my hover statement doesn't apply...So my question is, can I somehow live create this just like I was to use jquery.live() but with plain JS?
var parentContainer = document.getElementById('box');
var someContainer = document.createElement("div");
someContainer.className = "fun";
parentContainer.appendChild(someContainer);
jQuery("#box").hover(function() {
jQuery("#box .fun").fadeIn('fast');
});
So this obviously does not work because of the live bind issue...
Thanks for the help.
What needs to be live is the hover function, not the object.
Live means the event handler is placed at the root of the DOM tree, and if it matches your selector it will trigger.
Read more about how .live() works in jQuery's API documentation.

CKEditor: Class or ID for editor body

I have an instance of CKEditor on a page. I am trying to give the CKEditor's body a class or ID so it matches some styles I have defined in a stylesheet.
There is a API documentation that should give access to the respective DOM elements, but I can't seem to get it working. All objects I try to query that way turn out undefined.
Does anybody know how to do this, or how to properly address CKEditor's dom elements?
Edit: Thanks folks, nemisj's answer did it for me but for some reason, I don't get to set the "accepted" checkmark in this question.
Although that part of the API wasn't ported from 2.x at the time that this question was placed, now it's easier to use the bodyId and bodyClass config options.
Of course, the explanation by nemisj is good and can be useful for other things, but you must remember that each time that you switch away from design (to source view), the iframe is destroyed, so you'll need to reassign your attributes if you do it manually.
If you are talking about CKEditor( version 3), then there is a possibility to get any DOM instance inside the editor itself. Every CKEditor instance has reference to it's document via "document" property.
var documentWrapper = edit.document;
This reference represent some public wrapper for all CKEditor nodes, but it also has the direct reference to its node. You can retrieve by getting ["$"] property.
var documentNode = documentWrapper.$; // or documentWrapper['$'] ;
documentNode will represent the DOM instance of the document node inside the iframe. After you have the DOM instance, you can do whatever you want to do with DOM structure, Append, remove, replace classes, rebuild, etc. For example
documentNode.body.className = "zork";
I hope this should be enough.
I had the same problem today in trying to set the bodyClass like this:
CKEDITOR.replace( 'editor1',
{
fullPage : true,
bodyClass : 'myClass'
});
What I found is that in this version (3.3.1), if you set fullpage = true, setting the bodyId or bodyClass does not work, but if you set fullPage = false, it does work.
Hope this helps.
From the Manual:
<static> {String|Array} CKEDITOR.config.contentsCss
The CSS file(s) to be used to apply style to the contents. It should reflect the CSS used in the final pages where the contents are to be used.
config.contentsCss = '/css/mysitestyles.css';
config.contentsCss = ['/css/mysitestyles.css', '/css/anotherfile.css'];
Default Value:
<CKEditor folder>/contents.css
Don't know that editor, but as they all work the same way, you probably can't access the DOM elements created by the instance because they are created after the page has finished loading, and the DOM is ready as well. So, any new DOM elements added after that, theorically will not exist.
Still, you can try TinyMCE editor, wich has a "partnership" with jQuery javascript library to manipulate all you want, and the editor itself is pretty easy to change in almost every way.
Your queries may return undefined because the editor instances are placed inside an iFrame and your query is not looking there?
In config.js, write this code
config.bodyId = 'contents_id';
then you see body id in Ckeditor but when you want to access this id please use
$('#parent_id').find('iframe').contents().find('#within_iframe')
there $('#parent_id') means form_id or any parent which is simply way to access iframe. follow this code to access element in iframe

Categories

Resources