How to change jQuery default context? - javascript

I loaded jQuery on a Firefox addon, which causes it to load using XUL's window.
I want to change the default context so it will use the correct window object.
The classic example I always see is:
var $ = function(selector,context){
return new jQuery.fn.init(selector,context|| correctWindow );
};
$.fn = $.prototype = jQuery.fn;
But doing it I kill a lot of functions, like .ajax, .browser, ... I need these methods.
AMO won't let me create an wrapper to jQuery to send the correct window object.
So my only option is to somehow change jQuery's default context without changing its source code.
What options do I have?

On the first content script I added
var loader = Components
.classes["#mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://extensions/js/jquery-1.7.2.min.js");
var initjQuery = jQuery.fn.init;
$.fn.init = function(s,c,r) {
c = c || window.document;
return new initjQuery(s,c,r);
};
This way it works.

You change the default context with this wrapper:
jQuery.fn.init2 = jQuery.fn.init;
jQuery.prototype.init = function (selection, context, root) {
return new jQuery.fn.init2(selection, context, jQuery.context || root);
}
$(document).ready(function () {
jQuery.context = $(newContextSelector);
$("*").css("color", "red");
});
Look at context that is a jquery object.

Related

Testing multiple browsers with protractor backed by page objects

I'm writing a test where two browsers need to interact. The problem with simply forking the browser is that my page objects still reference the old browser. I didn't want to rewrite all of my PO's to take the browser as a parameter so I tried the first solution found in the link below where they overwrite the global variables with the new browser's version :
Multiple browsers and the Page Object pattern
However, changing the global variables doesn't seem to work as all the subsequent page object functions that I call are performed against the original browser instance. I have tried logging the window handler before and after the switch and they are indeed different which only baffles me further. Here's some of the code.
spec:
var MultiBrowserFunctions = require('../common/multiBrowserFunctions.js');
var HomePage = require('../home/home.po.js');
describe('blah', function(){
it('blah', function(){
MultiBrowserFunctions.openNewBrowser(true);
HomePage.initializePage();
});
});
MultiBrowserFunctions:
(function() {
var browserRegistry = [];
module.exports = {
openNewBrowser: function(isSameUrl){
if(typeof browserRegistry[0] == 'undefined'){
browserRegistry[0] = {
browser: browser,
element: element,
$: $,
$$: $$,
}
}
var tmp = browser.forkNewDriverInstance(isSameUrl);
var id = browserRegistry.length;
browserRegistry[id] = {
browser: tmp,
element: tmp.element,
$: tmp.$,
$$: tmp.$$,
}
switchToBrowserContext(id);
return id;
},
resetBrowserInstance : function(){
browserRegistry.splice(1,browserRegistry.length);
switchToBrowserContext(0);
}
}
function switchToBrowserContext(id){
console.log('---------------------------switching to browser: ' + id);
browser=browserRegistry[id].browser;
element=browserRegistry[id].element;
$=browserRegistry[id].$;
$$=browserRegistry[id].$$;
}
}());
My questions are:
(1) why doesn't this work?
(2) Is there some other solution that doesn't involve rewriting all of my po's?
What you can do is, save the browsers in different variables and then switch between them by overriding the globals via a utility or something.
describe('Switching browsers back and forth', function () {
var browserA, browserB;
it('Browser Switch', function () {
var browsers = {
a : browser,
b : browser.forkNewDriverInstance(true)
};
browserA = browsers.a;
browserB = browsers.b;
var browserAndElement = switchBrowser(browserB);
browser = browserAndElement.browser;
element = browserAndElement.element;
//do your stuff
var browserAndElement = switchBrowser(browserA);
browser = browserAndElement.browser;
element = browserAndElement.element;
//do your stuff
});
});
The switchBrowser() can look like following:
this.switchBrowser = function (currentBrowser) {
browser = currentBrowser;
element = currentBrowser.element;
return {
browser : browser,
element : element
}
}
In this way you don't have to rewrite your POs to take in the new globals.
Hope it helps!
Cheers

How to assign a function to a object method in javascript?

I'd like to 'proxy' (not sure if that's the term at all) a function inside a function object for easy calling.
Given the following code
function Soldier() {
this.el = $("<div></div>").addClass('soldier');
this.pos = this.el.position; // $(".soldier").position(), or so I thought
}
In the console:
s = new Soldier();
$("#gamemap").append(s.el); // Add the soldier to the game field
s.pos === s.el.position // this returns true
s.el.position() // Returns Object {top: 0, left: 0}
s.pos() // Returns 'undefined'
What am I doing wrong in this scenario and is there an easy way to achieve my goal (s.pos() to return the result of s.el.position()) ?
I thought about s.pos = function() { return s.el.position(); } but looks a bit ugly and not apropriate. Also I'd like to add more similar functions and the library will become quite big to even load.
When you're calling s.pos(), its this context is lost.
You can simulate this behavior using call():
s.pos.call(s); // same as s.pos()
s.pos.call(s.el); // same as s.el.position()
This code is actually ok:
s.pos = function() { return s.el.position(); }
An alternative is using bind():
s.pos = s.el.position.bind(el);
You can use the prototype, that way the functions will not be created separately for every object:
Soldier.prototype.pos = function(){ return this.el.position(); }
I'd recommend to use the prototype:
Soldier.prototype.pos = function() { return this.el.position(); };
Not ugly at all, and quite performant actually.
If you want to directly assign it in the constructor, you'll need to notice that the this context of a s.pos() invocation would be wrong. You therefore would need to bind it:
…
this.pos = this.el.position.bind(this.el);
It's because the context of execution for position method has changed. If you bind the method to work inside the element context it will work.
JS Fiddle
function Soldier() {
this.el = $("<div></div>").addClass('soldier');
this.pos = this.el.position.bind(this.el);
}
var s = new Soldier();
$("#gamemap").append(s.el);
console.log(s.pos());

How can I avoid having lots of conditional instantiations at the top of a sitewide javascript file?

I have a single JS file that I use across the whole of my app. The top of it looks like this:
$(document).ready( function() {
if( $('#element-a').length ) {
var featureA = new ElementAFeature( $('#element-a') );
}
if( $('#element-b').length ) {
var featureB = new ElementBFeature( $('#element-b') );
}
// repeat ad nauseam for elements C thru Z etc etc
});
// actual objects and logic go here
It works, but it's sort of ugly. Short of running different scripts on different pages, is there any way of tidying this up?
In each page do something like this
window.MYAPP = window.MYAPP || {};
window.MYAPP.element = $("page-element");
window.MYAPP.feature = new ElementXFeature(window.MYAPP.element);
then modify your init script to
$(document).ready( function() {
var feature = window.MYAPP.feature;
//Use feature here.
});
If you are writing a lot of specific init code for each page you might wanna consider having both a global init method and defining a local one for each page and pass any context needed from the global init.
window.MYAPP.initMethod = function(context) {}
//in global init
if (typeof window.MYAPP.initMethod === "function") {
window.MYAPP.initMethod({ pageSpecificSetting : 0});
}

JavaScript access elements from custom object

This must be a very stupid question, but I just can't get it to work.
I'm creating my own UIKit for iOS. (Website-kit which will allow iPhone-like interfaces).
But, I'm trying to create a JavaScript library, which can be used to change several elements of the document. For instance, set a custom background colour when the document loads.
I'm trying to do that with object-orientated JavaScript. Like this:
var UI = new Interface();
UI.setBackground("#000");
How could I achieve this?
(So the custom "UI" Object, and (an example) on how to change the background color of the document, from INSIDE the object.)
You can save a reference to the DOM inside the JS object and rewrite it as needed.
function Interface() {
this.setBackground = function (color) {
this.pointTo.style.background = color;
};
this.pointTo = document.body;
}
You can initialize this by:
var UI = new Interface();
UI.pointTo = document.getElementById('some_id');
UI.setBackground("#000");
// Set another style, on a different element
UI.pointTo = document.getElementById('some_other_id');
UI.setBackground("#FFF");
This is a simple implementation and need to be allot smarter, but it should do the job.
Edit:
Made a mistake in original posting, and fixed erroneous code. Also made an example: http://jsfiddle.net/HpW3E/
Like silverstrike's code, but you can pass the target object in the interface constructor to don't get trouble in the future.
function Interface(target) {
target = target || document.body;
this.setBackground = function (color) {
target.style.background = color || 'white';
};
}
Ok now you can do this:
var UI = new Interface(document.body);
UI.setBackground("#000");
or even in somecases that you are applying the interface in global scope !ONLY!:
var UI = new Interface(this.body);
UI.setBackground("#000");
Also will work as this:
var UI = new Interface();
UI.setBackground("#000");
Here is a very simple approach
// define the object
var Interface = function () {
var interface = document.getElementById("interface"); // just an example
// your new methods
this.setBackground = function (color) {
interface.style.backgroundColor = color;
}
// rest of your code
}
now you can make use of it
var UI = new Interface();
UI.setBackground("#000");

call function inside a nested jquery plugin

There are many topics related to my question and i have been through most of them, but i haven't got it right. The closest post to my question is the following:
How to call functions that are nested inside a JQuery Plugin?
Below is the jquery plugin i am using. On resize, the element sizes are recalculated. I am now trying to call the function resizeBind() from outside of the jquery plugin and it gives me error
I tried the following combinations to call the function
$.fn.splitter().resizeBind()
$.fn.splitter.resizeBind()
Any ideas, where i am getting wrong?
;(function($){
$.fn.splitter = function(args){
//Other functions ......
$(window).bind("resize", function(){
resizeBind();
});
function resizeBind(){
var top = splitter.offset().top;
var wh = $(window).height();
var ww = $(window).width();
var sh = 0; // scrollbar height
if (ww <0 && !jQuery.browser.msie )
sh = 17;
var footer = parseInt($("#footer").css("height")) || 26;
splitter.css("height", wh-top-footer-sh+"px");
$("#tabsRight").css("height", splitter.height()-30+"px");
$(".contentTabs").css("height", splitter.height()-70+"px");
}
return this.each(function() {
});
};
})(jQuery);
I had the same problem. Those answers on related posts didn't work for my case either. I solved it in a round about way using events.
The example below demonstrates calling a function that multiplies three internal data values by a given multiplier, and returns the result. To call the function, you trigger an event. The handler in turn triggers another event that contains the result. You need to set up a listener for the result event.
Here's the plugin - mostly standard jQuery plugin architecture created by an online wizard:
(function($){
$.foo = function(el, options){
// To avoid scope issues, use 'base' instead of 'this'
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Add a reverse reference to the DOM object
base.$el.data("foo", base);
base.init = function(){
base.options = $.extend({},$.foo.defaultOptions, options);
// create private data and copy in the options hash
base.private_obj = {};
base.private_obj.value1 = (base.options.opt1);
base.private_obj.value2 = (base.options.opt2);
base.private_obj.value3 = (base.options.opt3);
// make a little element to dump the results into
var ui_element = $('<p>').attr("id","my_paragraph").html(base.private_obj.value1 +" "+ base.private_obj.value2+" " +base.private_obj.value3);
base.$el.append(ui_element);
// this is the handler for the 'get_multiplied_data_please' event.
base.$el.bind('get_multiplied_data_please', function(e,mult) {
bar = {};
bar.v1 = base.private_obj.value1 *mult;
bar.v2 = base.private_obj.value2 *mult;
bar.v3 = base.private_obj.value3 *mult;
base.$el.trigger("here_is_the_multiplied_data", bar);
});
};
base.init();
}
$.foo.defaultOptions = {
opt1: 150,
opt2: 30,
opt3: 100
};
$.fn.foo = function(options){
return this.each(function(){
(new $.foo(this, options));
});
};
})(jQuery);
So, you can attach the object to an element as usual when the document is ready. And at the same time set up a handler for the result event.
$(document).ready(function(){
$('body').foo();
$('body').live('here_is_the_multiplied_data', function(e, data){
console.log("val1:" +data.v1);
console.log("val2:" +data.v2);
console.log("val3:" +data.v3);
$("#my_paragraph").html(data.v1 +" "+ data.v2+" " +data.v3);
});
})
All that's left is to trigger the event and pass it a multiplier value
You could type this into the console - or trigger it from a button that picks out the multiplier from another UI element
$('body').trigger('get_multiplied_data_please', 7);
Disclaimer ;) - I'm quite new to jQuery - sorry if this is using a hammer to crack a nut.
resizeBind function is defined as private so you cannot access it from outside of it's scope. If you want to use it in other scopes you need to define it like that
$.fn.resizeBind = function() { ... }
Then you would call it like that $(selector').resizeBind()
You have defined the resizeBind function in a scope that is different from the global scope. If you dont'use another javascript framework or anything else that uses the $ function (to prevent conflict) you can delete the
(function($){
...
})(jQuery);
statement and in this way the function will be callable everywhere without errors
I didn't test it:
this.resizeBind = function() { .... }

Categories

Resources