I have 2 main questions.
Does extending things like Object count?
What is DOM wrapping?
http://perfectionkills.com/whats-wrong-with-extending-the-dom/
After reading that article I couldn't find anything about DOM wrapping, and no specification and what exactly is and isn't DOM extension.
No, Object is specified as part of the Javascript language, while the DOM is an API only relevant in a browser environment and is used to "access and update the content, structure and style of documents" (W3C).
However, one of the reasons provided in that article arguing against the extension of DOM objects still applies to extending native types such as Object - namely the chance of collisions.
Wrapping an object refers to creating a new object that references the original, but providing additional functionality through the new, wrapper object.
For example, rather than extending a DOM Element object with a cross-browser addClass function like this:
var element = document.getElementById('someId');
element.addClass = function (className) {
...
};
You can instead define a wrapper function:
var ElementWrapper = function (element) {
this.element = element;
};
And add the function to its prototype:
ElementWrapper.prototype.addClass = function (className) {
...
};
And "wrap" elements like this:
var element = document.getElementById('someId');
var wrapped = new ElementWrapper(element);
wrapped.addClass('someClass');
Related
When using PageObjects for Protractor e2e tests, should you use getter functions for the element locator variables instead of having variables?
example:
public loginButton: ElementFinder = $('#login-submit');
public loginUsername: ElementFinder = $('#login-username');
Or should you use a getter function in the Page Object like this:
public get loginButton(): ElementFinder {
return $('#login-submit');
}
public get loginUsername(): ElementFinder {
return $('#login-username');
}
Is one approach better than another?
No getters needed, since protractor ElementFinder and ElementArrayFinder objects are lazy - no any searching for this element will be done until you will try to call some methods on them. Actually thats also a reason why you don't need to use await for protractor element search methods:
const button = $('button') // no await needed, no getter needed
console.log('Element is not yet tried to be searched on the page')
await button.click() // now we are sending 2 commands one by one - find element and second - do a click on found element.
http://www.protractortest.org/#/api?view=ElementFinder
ElementFinder can be used to build a chain of locators that is used to find an element. An ElementFinder does not actually attempt to find the element until an action is called, which means they can be set up in helper files before the page is available.
It's fine to use either but if you're going to transform or do something else to your element, then the getter function would be better in my opinion since that's the reason why we utilize mutators in the first place.
I'm not really sure which method is more convenient, if there is one.
I've been using protractor for a bit longer than 1 year now, and I always stored locators in variables.
What I'd normally do is:
const button = element(by.buttonText('Button'));
Then I'd create a function for interacting with the element:
const EC = protractor.ExpectedConditions;
const clickElement = async (element) => {
await browser.wait(EC.elementToBeClickable(element));
await element.click();
};
Finally, use it:
await clickElement(button);
Of course I store the locators and functions in a page object, and invoking/calling them in the spec file. It's been working great so far.
For the following JavaScript code, how can I write it in ReasonML?
class HelloWorld extends HTMLElement {
constructor() {
super();
// Attach a shadow root to the element.
let shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<p>hello world</p>`;
}
}
I could not find any documentation on writing classes in ReasonML? I cannot use plain objects/types as I need to extend from HTMLElement class which doesn't work with ES style classes.
I have looked into this existing question - How to extend JS class in ReasonML however, it is a different thing. To write web component, we need to extend HTMLElement and must call it with new keyword. ES5 style extension mechanism doesn't work.
You can't. Not directly at least, since BuckleScript (which Reason uses to compile to JavaScript) targets ES5 and therefore has no knowledge of ES6 classes.
Fortunately, ES6-classes require no special runtime support, but are implemented as just syntax sugar, which is why you can transpile ES6 to ES5 as shown in the question you link to. All you really have to do then, is to convert this transpiled output into ReasonML:
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var BaseElement = (function (_super) {
__extends(BaseElement, _super);
function BaseElement() {
_super.call(this);
}
return BaseElement;
}(HTMLElement));
And depending on what specific class-features you actually need, you can probably simplify it a bit.
What does it mean in Coffeescript when a variable name begins with an "#" sign?
For example, I've been looking through the hubot source code and just in the first few lines I've looked at, I found
class Brain extends EventEmitter
# Represents somewhat persistent storage for the robot. Extend this.
#
# Returns a new Brain with no external storage.
constructor: (robot) ->
#data =
users: { }
_private: { }
#autoSave = true
robot.on "running", =>
#resetSaveInterval 5
I've seen it several other places, but I haven't been able to guess what it means.
The # symbol is a shorcut for this as you can see in Operators and Aliases.
As a shortcut for this.property, you can use #property.
It basically means that the “#” variables are instance variables of the class, that is, class members. Which souldn't be confused with class variables, that you can compare to static members.
Also, you can think of #variables as the this or self operators of OOP languages, but it's not the exact same thing as the old javascript this. That javascript this refer to the current scope, which causes some problems when your are trying to refer to the class scope inside a callback for example, that's why coffescript have introduced the #variables, to solve this kind of problem.
For example, consider the following code:
Brain.prototype = new EventEmitter();
function Brain(robot){
// Represents somewhat persistent storage for the robot. Extend this.
//
// Returns a new Brain with no external storage.
this.data = {
users: { },
_private: { }
};
this.autoSave = true;
var self = this;
robot.on('running', fucntion myCallback() {
// here is the problem, if you try to call `this` here
// it will refer to the `myCallback` instead of the parent
// this.resetSaveInterval(5);
// therefore you have to use the cached `self` way
// which coffeescript solved using #variables
self.resetSaveInterval(5);
});
}
Final thought, the # these days means that you are referring to the class instance (i.e., this or self). So, #data basically means this.data, so, without the #, it would refer to any visible variable data on scope.
I can't find a way to create custom events with scala-js. For instance, with js you can create a custom event like the following (taken from here):
var event = new CustomEvent('build', { 'detail': elem.dataset.time });
However, there is no constructor for CustomerEvent or Event in scala-js that accept arguments. Also, subclassing either such as:
class DrawEvent extends Event {
override def `type` = "draw"
}
leads to
Uncaught TypeError: undefined is not a function
when trying to construct via new DrawEvent()
Any ideas?
To instantiate javascript classes in ScalaJs you have to use js.Dynamic.newInstance:
This should work for your use case:
val event = js.Dynamic.newInstance(js.Dynamic.global.CustomEvent)("build", js.Dynamic.literal(detail = elem.dataset.time)).asInstanceOf[js.dom.CustomEvent]
There is more info available at the remarks portion (all the way at the bottom) of:
http://www.scala-js.org/doc/calling-javascript.html
Here is the same solution using some imports to make it shorter
import js.Dynamic.{ global => g, newInstance => jsnew, literal => lit }
val event = jsnew(g.CustomEvent)("build", lit(detail = elem.dataset.time)).asInstanceOf[js.dom.CustomEvent]
If you want to stay in the typed DOM (assuming you are talking about the scala-js-dom library), you can do:
new CustomEvent().initCustomEvent('build', false, false, elem.dataset.time)
The constructor you are using is actually only specified in DOM 4 (see MDN).
What would be the most efficient way to find all ExtJS components that are rendered as descendants of a given HTML Element? Note that this Element is not part of a component itself and is not managed by Ext in any way, it is just hardcoded raw HTML. And this Element may not only contain just ext components, it may have other non-ext managed html in it as well, and that html can also contain ext components. So that means that the solution must traverse all the way down the DOM, not just look at direct children.
My suggestion would be to walk the dom, checking the id of each element against Ext.getCmp, which is a hash-map lookup. You could then switch to walking through the Component methods, but I think it would be basically the same speed, and if you're already walking the dom to begin with you might as well keep at it:
var dom = ...;
var components = [];
Ext.Array.each(Ext.get(dom).query('*'), function(dom) {
var cmp = Ext.getCmp(dom.id);
if (cmp)
components.push(cmp);
});
Ext.get(dom).query('*') may do more work than you'd like, it might be more efficient to have your own walker, like this:
function walk(dom, callback) {
if (dom.nodeType === 1) {
callback(dom);
Ext.Array.each(dom.childNodes, function(child) {
walk(child, callback);
});
}
}
var dom = ...;
var components = [];
walk(dom, function(dom) {
var cmp = Ext.getCmp(dom.id);
if (cmp)
components.push(cmp);
});
All this assumes that the dom ids match the component ids, which I don't know if that is something you can rely on in future versions of Ext.