Let's say that our script is included in a web-page, and a prior script (that already executed) did this:
String.prototype.split = function () {
return 'U MAD BRO?';
};
So, the split string method has been overwritten.
We would like to use this method, so we need to recover it somehow. Of course, we could just define our own implementation of this method and use that instead. However, for the sake of this question, let's just say that we really wanted to recover the browser's implementation of that method.
So, the browser has an implementation of the split method (in native code, I believe), and this implementation is assigned to String.prototype.split whenever a new web-page is loaded.
We want that implementation! We want it back in String.prototype.split.
Now, I already came up with one solution - it's a hack, and it appears to be working, but it may have flaws, I would have to test a bit... So, in the meantime, can you come up with a solution to this problem?
var iframe = document.createElement("iframe");
document.documentElement.appendChild(iframe);
var _window = iframe.contentWindow;
String.prototype.split = _window.String.prototype.split;
document.documentElement.removeChild(iframe);
Use iframes to recover methods from host objects.
Note there are traps with this method.
"foo".split("") instanceof Array // false
"foo".split("") instanceof _window.Array // true
The best way to fix this is to not use instanceof, ever.
Also note that var _split = String.prototype.split as a <script> tag before the naughty script or not including the naughty script is obvouisly a far better solution.
Related
I have an angular-application where I put custom html-attributes ("data-testid='saveButton'") for identification on e.g. buttons, inputs etc.
Protractor has no built-in locator for this: the xpath-way does not work as this will always search from the document root (to overcome this limitation I would have to do quite a lot of string-magic to build the xpath-string, so I discarded this variant).
So I wanted to write my own locator, and consulted the official Protractor Documentation.
The documentation leaves me with an important question:
What methods are available on the parent-element-object? In their example they use using.querySelectorAll('button'); but how do they know they can use this?
I searched for 'querySelectorAll' in the protractor-documentation to see if it is maybe a method of ElementFinder or WebElement, but I didn't find it.
Also I found no way to debug the locator (only stop the protractor-test by browser.pause()), so I can't look at runtime to see what I can do with the given parent-element.
So does anybody know how I can get information about what methods the parent-element has?
The parent-element, which is essentially the context of the selector is a DOM element that gets handled by the browser - it can use whatever DOM function there is, in this case Element.querySelectorAll. As the addLocator's example says about the function you pass to it:
This function will be serialized as a string and will execute in the browser.
Also, keep in mind that there is a way to provide context to the xpath selectors thus avoiding the search from root:
var context = element(by.css('.some-parent-element-class'));
var divDirectDescendants = context.element(by.xpath('div'));
var divAllDescendants = context.element(by.xpath('.//div'));
With CSS these would be:
var divDirectDescendants = element(by.css('.some-parent-element-class > div'));
var divAllDescendants = element(by.css('.some-parent-element-class div'));
Also, there's a way to achieve what you want with a CSS selector (which is what querySelectorAll is, after all):
var everyElementWithMyAttr = element.all(by.css('[data-testid="saveButton"]'));
Yo!
I have an arbitrary javascript file, let's call it localScript, and just say it looks something like this:
<script id="myScript" type="text/javascript">
function () {
var blue = 'blue';
var person = {
firstName:"John",
lastName:"Doe",
age:50,
eyeColor:"brown"
};
var bluePerson = function () {
person[color] = blue;
};
}
</script>
I want to be able to use another externalScript to dynamically change the contents of this localScript. For this simple example, let's just say I want to update some of the values in localScript, like—maybe change age of the person object to 75. (Obviously, there's very simple ways to do this, but for my use case it's imperative that I use another externalScript to generate the contents of this localScript).
It would be easy if there was something like .innerHtml which I could use in the externalScript which would allow me to select an element and then replace the 'innerHtml' contents. The localScript, though, obviously isn't composed of elements.
As far as I know, when using a script to modify another script, there aren't any 'easy' ways to reference variables/objects/items in the script.
Things I've considered are indexOf(), search(), and match(), which I could use in externalScript to find strings inside localScript and then replace the values. I feel though as these could be performance no-no's, especially if the script grows.
Are there any easy ways to do this—with an emphasis on performance? I feel like there must be some easy way to reference one of the items in the script, though, I suppose a script is all one large string.. and maybe there is no simple way.
BTW—I'm using AngularJS, if there are any built in methods—though I think this is mostly just a javascript thing.
Thanks a bunch!
It looks like a bad idea, but... well, if it is imperative...
It makes no sense to change a script in a <script> tag - if it is in DOM, it has already executed (and no longer matters). Thus, to change the script before it has a chance to execute, you need to load it using AJAX, change the text, then eval it.
You can easily change the variables. Refer following steps
Include external script just below the script you have written.
Access the variables in the external script as if they are locally declared.
The variables you have created in above script are available in global scope and hence should be accessible from everywhere.
Note: This answer was added before the function clause was added.
Background
I'm trying to refactor some long, ugly Javascript (shamefully, it's my own). I started the project when I started learning Javascript; it was a great learning experience, but there is some total garbage in my code and I employ some rather bad practices, chief among them being heavy pollution of the global namespace / object (in my case, the window object). In my effort to mitigate said pollution, I think it would be helpful to measure it.
Approach
My gut instinct was to simply count the number of objects attached to the window object prior to loading any code, again after loading third-party libraries and lastly after my code has been executed. Then, as I refactor, I would try to reduce the increase that corresponds to loading my code). To do this, I'm using:
console.log(Object.keys(window).length)
at various places in my code. This seems to work alright and I see the number grow, in particular after my own code is loaded. But...
Problem
Just from looking at the contents of the window object in the Chrome Developer console, I can see that its not counting everything attached to the object. I suspect it's not including some more fundamental properties or object types, whether they belong to the browser, a library or my own code. Either way though, can anyone think of a better and more accurate way to measure global namespace pollution that would help in refactoring?
Thanks in advance!
So after some of the comments left by Felix Kling and Lèse majesté, I have found a solution that works well. Prior to loading any libraries or my own code, I create the dashboard global object (my only intentional one) and store a list of objects attached to window via:
var dashboard = {
cache: {
load: Object.getOwnPropertyNames(window)
}
};
Then, after I load all of the libraries but prior to loading any of my own code, I modify the dashboard object, adding the pollution method (within a new debug namespace):
dashboard.debug = {
pollution: (function() {
var pollution,
base = cache.load, // window at load
filter = function(a,b) { // difference of two arrays
return a.filter(function(i) {
return !(b.indexOf(i) > -1);
});
},
library = filter(Object.getOwnPropertyNames(window), base),
custom = function() {
return filter(Object.getOwnPropertyNames(window),
base.concat(library));
};
delete cache.load;
pollution = function() {
console.log('Global namespace polluted with:\n ' +
custom().length + ' custom objects \n ' +
library.length + ' library objects');
return {custom: custom().sort(), library: library.sort()};
};
return pollution;
}())
};
At any point, I can call this method from the console and see
Global namespace polluted with:
53 custom objects
44 library objects
as well as two arrays listing the keys associated with those objects. The base and library snapshots are static, while the current custom measurement (via custom) is dynamic such that if I were to load any custom javascript via AJAX, then I could remeasure and see any new custom "pollution".
The general pattern you've selected works OK from experience. However, there are two things you might need to consider (as additions or alternatives):
Use JsLint.com or JSHint.com with your existing code and look at the errors produced. It should help you spot most if not all of the global variable usage quickly and easily (you'll see errors of 'undefined' variables for example). This is a great simple approach. So, the measurement in this case will be just looking at the total number of issues.
We've found that Chrome can make doing detection of leaking resources on the window object tricky (as things are added during the course of running the page). We've needed to check for example to see if certain properties returned are native by using RegExs: /\s*function \w*\(\) {\s*\[native code\]\s*}\s*/ to spot native code. In some code "leak detection" code we've written, we also try to (in a try catch) obtain the value of a property to verify it's set to a value (and not just undefined). But, that shouldn't be necessary in your case.
I've seen some JavaScript code to access HTML elements like this: elementID.innerHTML, and it works, though practically every tutorial I searched for uses document.getElementById(). I don't even know if there's a term for the short addressing.
At first I thought simplistically that each id'ed HTML element was directly under window but using getParent() shows the tree structure is there, so it didn't matter that elements I wanted were nested. I wrote a short test case:
http://jsfiddle.net/hYzLu/
<div id="fruit">Mango<div id="color">red</div></div>
<div id="car">Chevy</div>
<div id="result" style="color: #A33"></div>
result.innerHTML = "I like my " + color.innerHTML + " " + car.innerHTML;
The "short" method looks like a nice shortcut, but I feel there is something wrong with it for it practically not appearing in tutorials.
Why is document.getElementById() preferred, or may be even required in some cases?
Why shouldn't I access elements more “directly” (elemId.innerHTML)
Because, according to the others in this thread, referencing arbitrarily by id name is not fully supported.
So, what I think you should be doing instead is store their selections into a var, and then reference the var.
Try instead
var color = document.getElementById('color');
color.innerHTML = 'something';
The reason why this would be a good thing to do is that performing a lookup in the DOM is an expensive process, memory wise. And so if you store the element's reference into a variable, it becomes static. Thus you're not performing a lookup each time you want to .doSomething() to it.
Please note that javascript libraries tend to add shim functions to increase general function support across browsers. which would be a benefit to using, for example, jquery's selectors over pure javascript. Though, if you are in fact worried about memory / performance, native JS usually wins speed tests. (jsperf.com is a good tool for measuring speed and doing comparisons.)
It's safer I guess. If you had a variable named result in the same context that you are doing result.HTML I'm pretty sure the browser will throw a wobbler. Doing it in the way of document.getElementById() in this instance would obviously provide you with the associated DOM element.
Also, if you are dynamically adding HTML to the page I may be wrong, but you could also encounter unexpected behaviour in terms of what result is :)
Also I will add that not all ID's can have values that will not work as variable names. For instance if your ID is "nav-menu".
Although I suppose you could write window["nav-menu"].innerHTML
Which makes me think, what happens if you create a window level variable with the same name as an ID?
Checkout this jsfiddle (tested in chrome): http://jsfiddle.net/8yH5y/
This really seems like a bad idea altogether. Just use document.getElementById("id") and store the result to a variable if you will be using the reference more than once.
I have a problem with old website. All JavaScript code on it use getElemenById function. But tags of site markup doen't have id property, instead they have only name property. Although it still works for IE, browser returns elements even by name property. For all other browsers it's a mistake in JS.
I wonder if there any way to overload this function in other browser to make web site compatible to other browsers?
There's no way to "overload" a function in JavaScript in the sense that you would do so in a strongly-typed language like Java or C. In fact, every function in JavaScript is already overloaded in this sense in that you can call any function with any number and type of arguments.
What you can do, however, is insert your own proxy in front of the existing version, and implement the proxy with whatever behavior you prefer. For instance:
document._oldGetElementById = document.getElementById;
document.getElementById = function(elemIdOrName) {
var result = document._oldGetElementById(elemIdOrName);
if (! result) {
var elems = document.getElementsByName(elemIdOrName);
if (elems && elems.length > 0) {
result = elems[0];
}
}
return result;
};
I wouldn't count on overriding getElementById working properly. Sounds easy enough to do a search and replace that does something like this:
// Replace
document.getElementById("foo");
// With
myGetElementById("foo", document);
// Replace
myElement.getElementById("foo");
// With
myGetElementById("foo", myElement);
Then you can myGetElementById as you want, without worrying about what might happen in old IEs and what not if you override getElementById.
Try getElementsByName. This is used to get a collection of elements with respect to their name