Simple DOM manipulation in custom aurelia atttribute - javascript

I am experimenting with Aurelia custom attribute by testing a simple DOM manipulation.
To my surprise performing the manipulation by appending and ellipse node to the parent svg node does modify the HTML but doesn't render the ellipse.
Manipulating the innerHtml property does work as expected.
import { bindable, inject} from 'aureliaframework';
#inject(Element)
export class TestCustomAttribute {
constructor(private element: SVGElement) {
}
attached()
{
var ellipse = document.createElement("ellipse");
ellipse.setAttribute("cx","200");
ellipse.setAttribute("cy","200");
ellipse.setAttribute("rx","100")
ellipse.setAttribute("ry","100")
ellipse.setAttribute("style","fill:blue")
//this is rendered
this.element.innerHTML = "<ellipse style='fill: purple' cx='200' cy='200' rx='100' ry='100'></ellipse>"
//this shows on DOM explorer but not rendered
this.element.appendChild(ellipse)
}
Is it possible to achieve the desired result using appendNode() instead of manipulating the element innerHtml?

Looks like this is more of a quirk around the DOM API and SVG elements, rather than an issue with Aurelia.
Try using createElementNS and include the svg namespace instead https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
See this question for more details: jquery's append not working with svg element?

Related

Access Custom HTML Element methods in Angular

I'm using a framework similar to bootstrap in Angular that provides custom HTML elements, like , and I want to update the table with a click of the button. In my typescript file, I have code to get the element
(<HTMLTableElement>document.getElementById("resultTable")).updateRows()
and the issue I'm getting is that when I cast it to a HTMLElement, the methods that are predefined such as updateRows() don't work, and I get the error message that Property updateRows doesn't exist on HTML Element. Is there a way for me to call the custom methods so I can use the custom elements? Thanks so much!
(<HTMLTableElement>document.getElementById("resultTable")).updateRows()
Why not view child?
First add a reference to the html element in the component.html file
<result-table #resultTable>...</result-table>
Then you can get that element using viewChild
export class ExampleComponent {
#ViewChild('resultTable') resultTable: ElementRef<any>;
ngAfterViewInit() {
const element = this.resultTable && this.resultTable.nativeElement ? this.resultTable.nativeElement: this.resultTable;
element.updateRows();
}

How to bind css classes to angular2 elements, post-compiling?

If I insert the html code directly in the HTML obviously that the css is noted and the element is correctly displayed.
However, if I try to dinamically create the element in javascript and insert it the css classes won't be recognized.
For example, I've tried:
var el = document.createElement("div").setAttribute("class","some_class");
(...).appendChild(el).
And el does not obey to the css rules set by some_class
The only way I've been able to do this is directly change the element properties in javascript:
var el = document.createElement("div").style.width = "20px"; //this works
(...).appendChild(el).
The element is correctly styled.
Unfortunately I wanted to go by the class names / ids.
So, any idea on how to do this? How exactly can I bind the newly created elements in javascript via _ngcontent-vkl-6 tags created after angular2 compiling?
Use Renderer or Renderer2 (if using angular 4+):
constructor(private renderer: Renderer) {}
createElement() {
const element = this.renderer.createElement(theElementToAppendTo, 'div');
this.renderer.setElementAttribute(element, 'class', 'some_class');
}
The way you're doing it isn't safe for angular, DOM manipulation should only be done via the Renderer or other bindings such as HostBinding.
This example uses the Renderer:
https://angular.io/docs/js/latest/api/core/index/Renderer-class.html
But if you're using angular 4+ you should use Renderer2 which has a different api:
https://angular.io/docs/ts/latest/api/core/index/Renderer2-class.html
I hope this helps.

Do custom HTML elements inherit parent CSS styles?

When creating custom elements in HTML, does the child tag inherit the parent's CSS styles?
Here is my test case, from Chrome:
var h1bProto = document.registerElement ('h1-b',
{
prototype: Object.create (HTMLHeadingElement.prototype),
extends: "h1"
});
When I append a child using the new h1bProto it generates an H1 tag with is="h1-b", example below:
var node = document.body.appendChild (new hibProto());
node.textContent = "Hello";
<h1 is="h1-b">Hello</h1>
Hello
This gives me the parents CSS styles. However, if I add a node by creating the element first, then appending the node, the code looks like this:
var node = document.createElement ("h1-b");
node.textContent = "Hello";
document.body.appendChild (node);
<h1-b>Hello</h1-b>
Hello
Am I missing something, or do children not inherit the parent's CSS styles? If they don't, then is the best work around to use the Shadow DOM?
According to the W3 spec you aren't going crazy!
Trying to use a customized built-in element as an autonomous custom
element will not work; that is, Click
me? will simply create an HTMLElement with no special
behaviour.
Aka, in your example making a tag with <h1-b> will not apply the styling or behavior of an <h1> tag. Instead you must create an <h1> tag with the is attribute set to the name of your custom element. The section I linked you to in the spec actually does a great job explaining how to go about creating the tag.
All in all, you just need to make your element like so:
document.createElement("h1", { is: "h1-b" });
One reason that comes to mind for this is that most bots don't parse your javascript. As a result they would have a challenge to figure out what the elements in your dom really are. Imagine how much your seo would tank if a bot didn't realize that your <h1-b> elements were really <h1> elements!

Dojo - destroying tooltip widget when DOM node is destroyed

I use dijit/tooltips on a page that has a lot of domConstuct.destroy() and domConstuct.place() going on. So each time I remove some nodes from the DOM, I want to remove tooltips attached to those nodes. Currently the number of tooltip widgets is constantly growing on my page.
Is there a way to automatically remove a widget when corresponding DOM node is removed, or to check if existing tooltip widget's connect DOM node still exists?
You can attach a single Tooltip widget to multiple nodes at once, this may be the solution for you as you don't have to "manage" your tooltips anymore then. There's only one tooltip widget created for all tooltips, so you don't have to destroy it anymore.
The best way to achieve this is by using the selector property as described in the reference guide.
new Tooltip({
connectId: "myTable",
selector: "tr",
getContent: function(matchedNode){
return matchedNode.getAttribute("tooltipText");
}
});
If they don't have a common connectId and/or selector, then you can still use a single tooltip by adding the target to the same tooltip instance by using the addTarget() function.
To remove a target you can also use removeTarget() which accepts a DOM node (so you just pass the DOM node you want to remove).
If neither of these solutions is able to help you I'd like to know how you instantiate your tooltips, there are multiple ways to do that. For example by using connectId or by creating an ad hoc tooltip using the show() function.
I found a solution to my problem with a help of Dimitri's answer. I don't create separate Tooltip widget for each tooltip any more, now I put all the tooltips in one Tooltip using it's .addTarget() method. The second part of the solution is iterating through Tooltip's connectId property and checking if the DOM node still exists. I had to do it using Javascript native methods .contains() and .getElementById(), because Dojo's dom.byId() and query() gave me false positives. So, my code now looks like this:
// creating Tooltip
var tooltips = new Tooltip({
getContent: function(matchedNode){
return matchedNode.getAttribute("tooltiptext");
}
});
// adding tooltips
tooltips.addTarget(nameNode);
// deleting sufficient connects
for(var i = tooltips.connectId.length -1; i >= 0 ; i--){
if(!document.contains(tooltips.connectId[i]) && !document.getElementById(tooltips.connectId[i])){
tooltips.removeTarget(tooltips.connectId[i]);
}
}
The reason I had to use both .contains() and .getElementById() is that some of the nodes I attached tooltips to have ids and some don't, and Tooltip widget stores some of them as strings (id) and some as DOM nodes.

How can I set a css ":hover" on a DOM created element in JavaScript?

I'm using the DOM to manage a JSON response from an AJAX function I'm running. The script I'm writing needs to be completely portable, so I am defining the styles for the created elements on the fly (meaning, no linking to an external CSS, and no providing CSS in the HTML doc itself, because I won't have control of the doc).
I'd like to create a hover effect on some of the elements.
example:
#myDiv:hover { background:#000000; }
Is there a way to define that in the DOM? Or do I have to use mouseover?
You can dynamically create and manipulate stylesheets. See here for some of the cross-browser issues with this approach.
I've got a wrapper function lying around which works around some of them; using it, the code would read
document.createStyleSheet().addRule('#myDiv:hover', 'background:#000000;');
you may create element with predefined class:
.h:hover{color: #c00}
var elem = document.createElement('div');
elem.className = 'h'

Categories