Programmatically add/delete polymer element - javascript

Does the remove child method provided by polymer takes care of cleaning all the dependencies such as event listeners, binded vars, etc. ?
Polymer.dom(parent).removeChild(node)
I want to add/remove many elements programmatically and want to make sure nothing is leaking or nothing is still watching for objects that do not exist anymore.
To add:
// Create the element
var paperListbox = document.createElement('paper-listbox');
paperListbox.setAttribute('depth', depth);
this.listen(paperListbox, 'iron-select', 'selectionChanged');
// add it
Polymer.dom(this.$.container).appendChild(paperListbox);
To remove:
// get node of interest
var node0 = Polymer.dom(this.$container).childNodes[0];
Polymer.dom(this.$.container).removeChild(node0)
Does such a pattern would allow me to add/remove lot of elements properly?

Related

How to get all possible valid attributes on a DOM element [duplicate]

This question already has answers here:
How to list all element attributes in JS?
(3 answers)
Closed 5 years ago.
Please note that .attributes only gets the current attributes, which is not what this question is about.
I want a way to get all the attributes of a DOM element. Not just the ones that are on it now, but the ones that are possible in the future too.
The specific use case is to find the potential attributes in an SVGElement that aren't in an HTMLElement - there's a list on MDN (SVG Attribute reference), but, for obvious reasons, hardcoding is not a good idea.
My initial thought was to iterate through the prototype chain of an instance of each and compare the two lists (with basic filtering for event handlers), but this doesn't actually give the potential svg attributes.
EDIT
IMPORTANT NOTE - pasting your answer code into the console on this page and using document.body as a target should show a list of over 50 attributes, including things like contentEditable, contextMenu, dir, style, etc.
This also needs to work cross-browser.
Could something like this be what you're looking for?
It looks like a DOM element object stores empty keys for all possible attributes. Could you loop over these keys and store them in an array, then from there use something similar to filter out inherited attributes?
HTML
<svg id="blah"></svg>
Javascript
const blah = document.getElementById('blah')
let possibleKeys = []
for (let key in blah) {
possibleKeys.push(key)
}
Here's a JSBin example ... it looks like it produces a list of all possible attributes but it would need some filtering.
See also this thread.
How to list all element attributes in JS?
Any one of these should work because they return a live HTMLCollection.
var svgElement = window.document.getElementsByClassName("someSvgClass")[0];
//assume someSvgClass is on svg element.
//var svgElement = window.document.getElementsByTagName("svg")[0];
//var svgElement = window.document.getElementsByName("mySvg")[0];
//assume svg has this name.
var svgAttributes = svgElement.attributes;
for(let i=0; i<svgAttributes.length; i++) {
console.log(svgAttributes[i]);
}
See the below documentation from MDN on getElementsByTagName()
The Element.getElementsByTagName() method returns a live
HTMLCollection of elements with the given tag name. The subtree
underneath the specified element is searched, excluding the element
itself. The returned list is live, meaning that it updates itself with
the DOM tree automatically. Consequently, there is no need to call
several times Element.getElementsByTagName() with the same element and
arguments.
The documentation for getElementsByName , and getElementsByClassName say the same thing; a live node list is returned. If you'd like to try it, I created a fiddle here.
You'll see that svgAttributes list is automatically updated upon clicking "Add Attribute" without re-executing any of those functions.
There is no API for that and I don't think a workaround is possible because when you change an attribute on a current DOM node, the browser is responsible for re-rendering and updating the webpage in a low-level way that is not exposed to the JavaScript context.
Also, keep in mind that any correctly formatted attribute is actually valid in the context of a DOM tree, even though it might not trigger any change at the rendering level or in the way the browser renders the page. Especially the data-* attributes.
There might be some vendor-specific API but that wouldn't be useful if you want cross-browser compatibility.
You need to hardcode it, sadly. Given that you specifically want the SVGElement attributes, maybe you can scrape the W3's SVG standard document to automatically create the list?
Edit: I made a snippet to easily scrape the values from the standard:
const uniq = arr => Array.from(new Set(arr))
const nameElements = document.querySelectorAll('table[summary="Alphabetic list of attributes"] .attr-name')
const arrNameElements = Array.prototype.slice.call(nameElements)
const svgAttributes = uniq(arrNameElements.map(el => el.innerText.replace(/\‘|\’/g, '')))
Just execute it on the svg attributes page, by opening the dev console on the page and pasting in this code :)
Edit 2: I forgot the presentation attributes. I'll let you figure that one out ;)

Add an HTML class to a DOM element controlled by two.js

I'm trying to add a class and ID to specific Two.js objects in this project: http://itpblog.evejweinberg.com/Homework/Open/ (click a few times to play)
If I console.log the word 'background' I see that these are two.js objects but I can't seem to use css or jquery to add a class/ID to them.
Any help is appreciated!
I even tried adding it to the whole project but that did not work:
$(document.body).addClass("dropshadow");
two.js entities are not DOM elements themselves, but each Scene, Group, or Polygon contains at least one reference to the DOM element that gets drawn when the entity is changed. To reference various DOM elements use the following syntaxes:
// Initialize two.js and attach to a dom element referenced by `canvas`
var two = new Two(params).appendTo(canvas);
// Two.Scene
var my_scene = two.renderer.domElement;
// Two.Group
var my_group = document.getElementById(two.scene.id);
// Two.Polygon — requires knowing the ID by means of your custom app logic.
var my_poly = document.getElementById(my_poly_html_id);
my_poly.classList.add('my-class');
Here's a screenshot showing all three commands in an actual app along with the outcome of each, with one additional command add a class to the shape that was targeted. The syntax of the last command differs but I omitted the var statements so that the console would display the result instead of outputting undefined.
If you'd like to create custom HTML IDs for individual shapes, use the .id setter before the initial render of your shape. Since most of this code is just setup, I offer a practical example on one of my own projects. In that snippet, a shape variable holds a new instance of Two.Polygon so calling shape.id = 'something-unique' before calling two.update() to draw the shape for the first time results in a DOM element with a custom HTML ID. Here is an incomplete block of setup code showing how to set the ID:
// Create new shape
var shape = two.makeRectangle(START_X, START_Y, START_WIDTH, START_HEIGHT);
// Set custom ID
shape.id = 'shape-' + Math.random(10000000);
// Draw shape for first time.
two.update();

Knockoutjs cleanup on DOM removal

I will be using knockoutjs in a single page application and I am concerned whether the observables remain in memory even if any DOM elements that the bindings were applied to, are removed.
What I think is that I will need to handle this in my application, by calling
ko.cleanNode(DOMElement)
on each DOM element that used observables, before removing them from the document.
I just need someone to confirm that this is the case
Thanks
CleanNode doesn't remove the observables, it just unbinds them from the UI elements. You'd also need to remove any references to your view models by setting them to null in order for them to be garbage collected, something like:
var myVM = new myViewModel();
ko.applyBindings(myVM, DOMElement);
//All your other stuff
ko.cleanNode(DOMElement);
myVM = null;

Using $.data() in jQuery to store flag on element

I am authoring a simple jQuery plugin that turns an input tag into a time-formatted element (on blur it will change 245p into 2:45 pm).
Since I do not want to apply the time format events to the same element twice, I need a way to detect that the specific element in the list provided has not already had the format applied.
This is the relevant part of the code:
var methods = {
init : function(sel) {
var $this = $(sel);
return $this.each(function(){
var data = $(this).data('time_formatted');
if (data) {
return;
} else {
$(this).data('time_formatted', true);
I have heard that using $(sel).data() in a plugin is not a good idea; instead, use $.data(). I don't know why, that's just what I've heard; honestly, I don't know what the difference is.
So my question is, is this the way to accomplish checking if a specific element has had the time formatter applied to it in a plugin?
If you care to see the plugin in it's current development state, see http://jsfiddle.net/userdude/xhXCR/.
Thanks!
Jared
Where have you heard that using .data() is not good? jQuery's plugin autoring page says:
Often times in plugin development, you may need to maintain state or check if your plugin has already been initialized on a given element. Using jQuery's data method is a great way to keep track of variables on a per element basis. However, rather than keeping track of a bunch of separate data calls with different names, it's best to use a single object literal to house all of your variables, and access that object by a single data namespace.
So it should be perfectly fine.

Rendering suggested values from an ext Combobox to an element in the DOM

I have an ext combobox which uses a store to suggest values to a user as they type.
An example of which can be found here: combobox example
Is there a way of making it so the suggested text list is rendered to an element in the DOM. Please note I do not mean the "applyTo" config option, as this would render the whole control, including the textbox to the DOM element.
You can use plugin for this, since you can call or even override private methods from within the plugin:
var suggested_text_plugin = {
init: function(o) {
o.onTypeAhead = function() {
// Original code from the sources goes here:
if(this.store.getCount() > 0){
var r = this.store.getAt(0);
var newValue = r.data[this.displayField];
var len = newValue.length;
var selStart = this.getRawValue().length;
if(selStart != len){
this.setRawValue(newValue);
this.selectText(selStart, newValue.length);
}
}
// Your code to display newValue in DOM
......myDom.getEl().update(newValue);
};
}
};
// in combobox code:
var cb = new Ext.form.ComboBox({
....
plugins: suggested_text_plugin,
....
});
I think it's even possible to create a whole chain of methods, calling original method before or after yours, but I haven't tried this yet.
Also, please don't push me hard for using non-standard plugin definition and invocation methodics (undocumented). It's just my way of seeing things.
EDIT:
I think the method chain could be implemented something like that (untested):
....
o.origTypeAhead = new Function(this.onTypeAhead.toSource());
// or just
o.origTypeAhead = this.onTypeAhead;
....
o.onTypeAhead = function() {
// Call original
this.origTypeAhead();
// Display value into your DOM element
...myDom....
};
#qui
Another thing to consider is that initList is not part of the API. That method could disappear or the behavior could change significantly in future releases of Ext. If you never plan on upgrading, then you don't need to worry.
So clarify, you want the selected text to render somewhere besides directly below the text input. Correct?
ComboBox is just a composite of Ext.DataView, a text input, and an optional trigger button. There isn't an official option for what you want and hacking it to make it do what you want would be really painful. So, the easiest course of action (other than finding and using some other library with a component that does exactly what you want) is to build your own with the components above:
Create a text box. You can use an Ext.form.TextField if you want, and observe the keyup event.
Create a DataView bound to your store, rendering to whatever DOM element you want. Depending on what you want, listen to the 'selectionchange' event and take whatever action you need to in response to the selection. e.g., setValue on an Ext.form.Hidden (or plain HTML input type="hidden" element).
In your keyup event listener, call the store's filter method (see doc), passing the field name and the value from the text field. e.g., store.filter('name',new RegEx(value+'.*'))
It's a little more work, but it's a lot shorter than writing your own component from scratch or hacking the ComboBox to behave like you want.
#Thevs
I think you were on the right track.
What I did was override the initList method of Combobox.
Ext.override(Ext.form.ComboBox, {
initList : function(){
If you look at the code you can see the bit where it renders the list of suggestions to a dataview. So just set the apply to the dom element you want:
this.view = new Ext.DataView({
//applyTo: this.innerList,
applyTo: "contentbox",
#qui
Ok. I thought you want an extra DOM field (in addition to existing combo field).
But your solution would override a method in the ComboBox class, isn't it? That would lead to all your combo-boxes would render to the same DOM. Using a plugin would override only one particular instance.

Categories

Resources