'use strict';
var Controller = function Controller() {};
Controller.init = function() {
if (!Controller.PROPERTIE.element || !Controller.PROPERTIE.indicator) {
return 'Expected to find [data-controller] and [data-controller-to]';
}
Controller._start();
};
Controller.PROPERTIE = {
element: $('[data-controller]'),
indicator: $('[data-controller-to]'),
state: false
};
Controller._start = function() {
Controller.PROPERTIE.indicator.bind('click', Controller._toggle);
};
Controller._toggle = function() {
Controller.PROPERTIE.element
.animate({
bottom: (Controller.PROPERTIE.state = !Controller.PROPERTIE.state) ? '-110' : '0'
});
};
apparently the elements in the object property does not exist, but they do exist ! Could someone tell me if I can not use javascript like that?
Maybe there is something with hoisting that is breaking the script?
I already try put the object before the init and the result is the same.
I know that i can extend prototype, but i have my reasons to use like this.
Thanks.
I can think of three possible causes:
The code is being initialized before the DOM has been parsed.
Your HTML does not actually contain elements that will match these selectors.
The desired elements are being created dynamically after this code is being initialized.
In this declaration:
Controller.PROPERTIE = {
element: $('[data-controller]'),
indicator: $('[data-controller-to]'),
state: false
};
the two jQuery expressions will be evaluated immediately as this code is parsed. If this code is not placed at the very end of the body, then those elements will likely not exist yet.
The usual solution to this is to not initialize those elements in a static declaration, but to initialize them by calling a function after the page has been loaded.
If you really intend for them to be globals, then you could just do this:
$(document).ready(function() {
Controller.PROPERTIE = {
element: $('[data-controller]'),
indicator: $('[data-controller-to]'),
state: false
};
});
But, you will also have to make sure that you don't try to use them until after this code runs.
The only other possible cause I can think of is that your DOM does not contain elements that match your two selectors. It seems like it's likely one of these two issues. Since you haven't shown us either the overall page structure of the HTML you intend to match, it's impossible for us to anything other than tell you what causes it might be.
I have a feeling you are thinking the jQuery object is not defined because of this:
if (!Controller.PROPERTIE.element || !Controller.PROPERTIE.indicator) {
return 'Expected to find [data-controller] and [data-controller-to]';
}
You must remember that jQuery constructor $() will always return an object. If you really want to test if there's a selected item you must use the length property of the jQuery object.
if (!Controller.PROPERTIE.element.length || !Controller.PROPERTIE.indicator.length) {
//element or indicator not there.
return 'Expected to find [data-controller] and [data-controller-to]';
}
Related
I have following issue concerning understanding ember properties:
If i have a propertyA:
propertyA: function() {
return this.get("propertyB.someObject.someValue");
}.property("propertyB")
and a propertyB:
propertyB: function() {
return this.get("propertyX.someObject");
}.property("propertyX", "propertyY", "propertyZ")
And i have a binding for propertyA in some template like:
{{propertyA}}
Then in 90% of the cases in my code it happens that propertyA does not get updated properly when i set i.e. propertyX.
If i understand it correctly, then propertyB should become dirty as soon as one of the dependent properties (like propertyX) changes. This should automatically make propertyA dirty and thus update it automatically since it has a binding.
What happens in my code is, that propertyA remains the old cached value even when i called it in the console, but when i call propertyB it revaluates and returns the updated code, since it was dirty.
The question is, why does propertyA not automatically become dirty when propertyB does? Is it because propertyB has no binding in a template? I thought it is not necessary if propertyA has the dependence.
I also figured out that this problem does not occur when propertyB just depends on propertyX, so the multi-dependency must somehow mess things up.
Sorry for this quite complicated explanation but i tried to abstract my actual code as simple as possible.
UPDATE:
Ok here some actual code:
Controller:
styling: function() {
var clipValues = this.get("clip.styling") || {};
var infoValues = this.get("clip.info.styling") || {};
return Ember.Object.create(jQuery.extend({}, clipValues, infos));
}.property("clip.styling", "clip.info.styling"),
showBottombar: function() {
return (!!this.get("bottombarSrc") || !!this.get("styling.bottombar.fancyStuff"));
}.property("styling"),
Somewhere else the clip gets set for this controller. And later its info gets updated in the clip model which is a simple Ember.Object:
getInfo: function(url) {
var self = this;
return App.ajax(url).then(function(response) {
self.set("info", response);
});
}
Now after getInfo gets called, the {{showBottombar}} in the template shows "false" even if "bottombarSrc" and "...fancyStuff" is true. When i call "styling" from the console, it reevaluates the styling code which indicates that it was marked as dirty after clip.getInfo happened (which sets the "info"). But this does not effect the showBottombar. It just does not get called afterwards.
UPDATE 2
There are two strange ways of making it work, but i dont understand why:
First one is adding a styling binding to a template:
{{styling}}
That causes showBottombar to get called after the styling changes.
Second one is removing other dependencies from the styling property:
styling: function() {
var clipValues = this.get("clip.styling") || {};
var infoValues = this.get("clip.info.styling") || {};
return Ember.Object.create(jQuery.extend({}, clipValues, infos));
}.property("clip.info.styling"),
(no more "clip.styling" dependency). Which also causes the showBottombar property to work properly. Both ways work individually.
propertyA: function() {
return this.get("propertyB.someObject.someValue");
}.property("propertyB").volatile()
http://emberjs.com/api/classes/Ember.ComputedProperty.html#method_volatile
I'm trying to detect whether a custom element with a specific name was registered or not. Is there a way to make such check?
Or is there a way to get list of registered custom elements?
I do document.registerElement, but what else is there? Is it one-way API?
There is a way to check whether an element was registered. Registered elements have their own constructors, while unregistered ones would use plain HTMLElement() for constructor (or HTMLUnknownElement() whether the name is not valid, but this is out of scope of the question):
document.registerElement('x-my-element');
document.createElement('x-my-element').constructor
//⇒ function x-my-element() { [native code] }
document.createElement('x-my-element-not-registered').constructor
//⇒ function HTMLElement() { [native code] }
That said, the checker might look like:
var isRegistered = function(name) {
return document.createElement(name).constructor !== HTMLElement;
}
Or, with syntactic sugar:
String.prototype.isRegistered = function() {
return document.createElement(this).constructor !== HTMLElement;
}
'x-my-element'.isRegistered()
//⇒ true
'xx-my-element'.isRegistered()
//⇒ false
The mostly careful version:
String.prototype.wasRegistered = function() {
switch(document.createElement(this).constructor) {
case HTMLElement: return false;
case HTMLUnknownElement: return undefined;
}
return true;
}
'x-my-element'.wasRegistered()
//⇒ true
'xx-my-element'.wasRegistered()
//⇒ false
'xx'.wasRegistered()
//⇒ undefined
There is no way to access a list of registered elements, AFAIK.
BTW, I still think that the try-catched registration (as proposed by #stephan-muller) suits your needs better.
Since custom elements is now part of the latest standard, I thought I'd share how to do this in 2017+:
Note: the document.registerElement function has been deprecated in favor of customElements.define().
customElements is defined as a global in window. There are three methods defined:
define
get
whenDefined
get is the important one here. get takes a string of the element name and returns the constructor for the named custom element, or undefined if there is no custom element definition for the name.
So in 2017+ to check if an element has been registered do you:
const myElementExists = !!customElements.get('my-element');
I'm not sure if there's a way to get a list of defined elements however.
NOTE: this doesn't work in IE. See here for browser compatibility
EDIT (2021): This no longer works as /deep/ was removed a few years ago.
Combining a few of the above approaches you can iterate over everything in use and spit out a unique list of custom (and registered) elements:
function isRegistered(name) {
return document.createElement(name).constructor.__proto__ !== window.HTMLElement;
}
var allElems = document.querySelectorAll('html /deep/ *');
var nodeNames = [].map.call(allElems, el => el.nodeName.toLowerCase())
.filter((value, index, self) => self.indexOf(value) === index)
console.log('all elements', nodeNames);
console.log('registered, custom elements', nodeNames.filter(isRegistered))
There doesn't seem to a way to see all registered elements at the moment, but there is a way to check whether or not an element has been registered already: wrap the register in a try...catch block:
try {
document.registerElement('x-my-element');
} catch(e) {
console.log('already exists', e);
}
Run this twice in your console and you'll see the error logged.
This does have a drawback if you simply want to check whether or not it was registered though: if it was not, it will be after running this. There also isn't a way to unregister an element it seems.
While I am not sure it applies to other Web Component frameworks, while using Polymer in Chrome, I have a CustomElements object to the window object. The CustomElements object has a key/value collection of all registered custom elements called registry.
function isRegistered(name) {
if (window.CustomElements && window.CustomElements.registry)
return name in window.CustomElements.registry;
return undefined;
}
As written already on the Polymer's Slack channel, this is a dirty one that can make the job:
function isElementRegistered(elementId) {
return Polymer.telemetry.registrations.find(function(item) { return item.is === elementId })
}
Not sure how much Polumer.telemetry.registrations is reliable though (haven't seen it on the doc) and Array.prototype.find is not cross-browser!
Here is one way you get get a list of all the registered customElements using ES6
// Get all elements
const elements = document.querySelectorAll('*')
// Create an array from elements
const elementArray = Array.from(elements)
// Map to node names
const nodeNames = elementArray.map(element => element.nodeName.toLowerCase())
// Filter by which ones are registered
const allCustomElementNames = nodeNames.filter(customElements.get.bind(customElements))
in scenarios where custom element classes (constructors) self-register an element, it is sufficient to check for the presence of the class
I was looking for a list of all registered custom elements. Based on answers here, came up with something like this:
let original = customElements.define;
window.customTags = [];
customElements.define = function () {
console.log("custom elements hijacked", arguments);
window.customTags.push({ tag: arguments[0], ctor: arguments[1] })
original.apply(customElements, arguments);
}
setTimeout(() => {
console.log(window.customTags);
}, 1000)
I am trying to use enquire.js to trigger a reload of my bxslider when the screen size is small, to show fewer images.
I have registered a screen width as below.
enquire.register("screen and (max-width:900px)", {
match: this.ChangeSliderDown,
unmatch:this.ChangeSliderUp
});
Now as part of the transition i need to do a calculation based on a variable that is associated with the Prototype of the current class.
ChildCarousel.prototype = {
...
ChangeSliderUp: function()
{
var maxSlides = (this.ourCarouselCollection.length < 3) ? 1 : 3;
...
}
}
in all my other functions referring to this allows me to access variables such as the ourCarouselCollection in the instance of enguire js i get the object that is a result of the register call.
why is this happening and is it possible to change it?
adding the bind(this method solved the problem
enquire.register("screen and (max-width:900px)", {
match: this.ChangeSliderDown.bind(this),
unmatch:this.ChangeSliderUp.bind(this)
});
The value of this has nothing to do with scope, it is resolved within an execution context and is set by the call or with bind. Also, it is convention that only functions that are intended to be called as constructors have names that start with a capital letter (so ChangeSliderUp should be changeSliderUp).
The ChangeSliderUp method is expecting to be called with this referencing an instance of ChildCarousel as its this. When you assign a reference to the function like:
match: this.ChangeSliderDown
then the function will be called without this being set to the instance and will default to the global object or be undefined in strict mode.
You can use bind per Bluephlame's answer, or use a closure something like:
// Assuming that this referenes an instance of ChildCarousel
// where this code is running
var carousel = this;
enquire.register("screen and (max-width:900px)", {
match: function() {carousel.ChangeSliderDown();},
unmatch: function() {carousel.ChangeSliderUp();}
});
but I can't test that. It should ensure that the function is called as a method of an instance, hence setting this to the instance.
I am relatively new to javascript so please be patient if what i am asking is completely stupid!
I am trying to make a simple module. Inside the module i want to have a config object that holds settings for the module. I am also using jquery. The jquery selectors work only when in a function directly in the main object/module.
I understand that javascript has functional scope so I am suprised that I cannot use the jquery selectors anywhere inside the module.
EDIT:
I want to be able to directly set all of my configs inside the configs object using jquery selectors. This way i keep all the messy stuff inside one place and can then access configs.whatever throughout the rest of the module. At the moment jquery selectors do not work inside the configs module.
var OB = function() {
var configs = {
'mode' : 'test',
'numOfSelects' : $('.mySelect').find('select').length, // This doesnt work
}
var getMode = function() {
return configs.mode;
}
function init() {
alert(configs.numOfSelects); // This alerts 0 until the following line
alert($('.mySelect').find('select').length); // This correctly alerts 2
};
var handlers = {
successHandler : function() {
alert("Success");
},
errorHandler : function() {
alert("error");
}
}
return {
init : init,
getMode : getMode
}
}( );
$(document).ready(function(){
OB.init();
});
It isn't that jQuery isn't in scope — that's that the code isn't executing when you think it is. The variable config is defined when that anonymous function (var OB = function() {}()) is executed. The DOM isn't ready yet, so that DOM traversal doesn't find anything. When you do the DOM traversal in init(), that isn't executed until it's explicitly called inside the $(document).ready() handler, at which point that DOM is set up. That's the difference you're seeing.
OB() needs to be called after the DOM has completely loaded. Hence the answer by Marcelo, which calls OB() in the ready() method.
EDIT: It's funny that my original answer below was incorrect because I didn't notice two little parentheses at the end of the definition of OB, and it turns out that these are the culprit. You define and then immediately invoke OB, which is before the DOM has been fully loaded. Remove those parentheses and make the change I suggest below.
Calling OB() returns an object with init and getMode, but you haven't called OB(), you've only referred to OB. Try this instead:
$(document).ready(function(){
OB().init();
});
Also, I assume you want to later refer to getMode. In particular, you will to get the copy of getMode that has access to the same local scope that your init() call had access to. To achieve this, you will need to store the result of calling OB() for later use:
var ob;
$(document).ready(function(){
ob = OB();
ob.init();
});
function some_other_function() {
... ob.getMode() ...;
}
I know. It is possible to dynamically load JavaScript and style sheet file into header of document. In the other hand, it is possible to remove script and style sheet tag from header of document. However, loaded JavaScript is still live in memory.
Is it possible to destroy loaded JavaScript from web browser memory? I think. It should be something like the following pseudo code.
// Scan all variables in loaded JavaScript file.
var loadedVariable = getLoadedVariable(JavaScriptFile);
for(var variable in loadedVariable)
{
variable = null;
}
// Do same thing with function.
Is it possible to create some JavaScript for doing like this?
Thanks,
PS. Now, you can use xLazyLoader and jQuery for dynamic loading content.
If the loaded script is assigned to a window property, for instance with the module pattern like so:
window.NiftyThing = (function() {
function doSomething() { ... }
return {
doSomething: doSomething
};
})();
or
window.NiftyThing = {
doSomething: function() { ... }
};
or
NiftyThing = {
doSomething: function() { ... }
};
Then you can delete the property that references it:
delete window.NiftyThing;
...which removes at least that one main reference to it; if there are other references to it, it may not get cleaned up.
If the var keyword has been used:
var NiftyThing = {
doSomething: function() { ... }
};
...then it's not a property and you can't use delete, so setting to undefined or null will break the reference:
NiftyThing = undefined;
You can hedge your bets:
NiftyThing = undefined;
try { delete NiftyThing; } catch (e) { }
In all cases, it's up to the JavaScript implementation to determine that there are no outstanding external references to the loaded script and clean up, but at least you're giving it the opportunity.
If, as Guffa says, the loaded script doesn't use the module pattern, then you need to apply these rules to all of its symbols. Which is yet another reason why the module pattern is a Good Thing(tm). ;-)
It might be possible to remove a Javascript file that has been loaded, but that doesn't undo what the code has done, i.e. the functions that was in the code are still defined.
You can remove a function definition by simply replacing it with something else:
myFunction = null;
This doesn't remove the identifier, but it's not a function any more.