Im trying to modify the behavior of the jquery UI spinner, and want to avoid hacking the original js file. Theres a function in the js file called _doSpin that if I modify I can achieve the effect I want (I want it to change the value by a percentage - not like the built in percentage method which presents a real percentage to the user, but I need normal ints to be incremented or decremented by a percentage without the bounds of 100% to 0%).
But I don't want to hack the original file, so I want to override the function from within my page (which uses ui.spinner.js). I cant work out how to get to the namespace of the library file, from within my html page. I've tried loads of tips from stackoverflow already but none seem to work in my case. The library file starts with (function($, undefined) but I can't understand how its defined a namespace for me to define in my html page, so I can 'break in' and override the function.
BTW the function is used internally in the library file, maybe its not possible to override it in this case? I don't want to use it locally in my html page, I call higher methods to do my business, I just need to change how the library works internally.
First off, are you entirely sure that you can't solve your problem by specifying options to the spinner object? It seems that with the min, max and step options you could make the step be any percentage of the range that you wanted at the time you created the spinner or set the range. Here's the doc for the step option.
If this is an internal function (not exposed in a public API or as a method on a public object), then it all depends upon how it is coded. If it is not put into a public namespace and it is defined within some other function, then you can't get at it to redefine it. The internals of a function are not something you can get to from outside the function in Javascript unless they are explicitly make accessible to the public somehow.
If it is in a public API somewhere, then you can just assign a new function to whatever symbol the API is on and that will replace the original function (usually you save the original function first so you can still use it if needed).
We can't really help you more specifically without you showing us the actual code you're wanting to override. FYI, I can't find a function called _doSpin in the jQueryUI source repository, so I don't really know which function in which code you're trying to override.
Edit: In a Google search, I found another version of a jQuery UI spinner that does have a _doSpin() method. In that version the _doSpin() method ends up as a method on the prototype for the spinner widget object (which is a jQuery UI object structure it uses for all its objects). I don't personally have much experience with jQueryUI, but from reading their doc on the widget factory, it sounds like you can inherit from an existing widget and replace methods on the prototype. Or, you could probably hack on the prototype directly - though it would take some doing to figure out how/where jQueryUI stores it or how to get to it.
This did a better job in the end.
<html>
<script type='text/javascript'>
var mainarray = [1,2,3,4,5];
function scrolltxtbox (e) {
var evt=window.event || e;
var delta=evt.detail ? evt.detail*(-120) : evt.wheelDelta;
var targ;
if (evt.target) targ = evt.target;
else if (evt.srcElement) targ = evt.srcElement;
(delta<=-120)?multiplier=0.95:multiplier=1.05;
changeweight(targ.id, multiplier);
if (evt.preventDefault)
evt.preventDefault()
else return false
}
function changeweight(targid, multiplier) {
var u = targid.match(/myspinner(.*)/);
mainarray[u[1]] = mainarray[u[1]]*multiplier;
document.getElementById(targid).value = mainarray[u[1]].toPrecision(3);
}
var mousewheelevt=(/Firefox/i.test(navigator.userAgent))? "DOMMouseScroll" : "mousewheel"
var index;
for (index = 0; index < mainarray.length; ++index) {
document.write('<input type="text" size=6 id="myspinner'+index+'" value='+mainarray[index]+' autocomplete="off"><br>');
if (window['myspinner'+index].attachEvent) {
window['myspinner'+index].attachEvent("on"+mousewheelevt, scrolltxtbox);
} else if (window['myspinner'+index].addEventListener) {
window['myspinner'+index].addEventListener(mousewheelevt, scrolltxtbox, false);
}
}
</script>
</html>
Its probably (definitely!) oververbose, but it works nice.
Related
(new to JS, jQuery, & jqTree)
I am trying to override a method (JqTreeWidget.prototype.openNode) from one .js file (tree.jquery.js) in another (my own custom.js).
I've read that to override a js method in general, I just need to redefine it. So I am trying to do that on the method, and I think I am stuck on accessing the variable that has the original method (JqTreeWidget). I think the challenge is that the original method is in tree.jquery.js (source) that is separate from my own other custom.js file where I want to do the override.
The goal of this Question would be to allow me to write something like this in my custom.js (<reference to JqTreeWidget.prototype.openNode> would be the Answer to this Question):
var originalMethod = <reference to JqTreeWidget.prototype.openNode>;
// Override of originalMethod
<reference to JqTreeWidget.prototype.openNode> = function( node, slide ){
// my code I want to happen 1st here
changeAncestorHeightRecursively( node, true);
// my code is done, and now I'm ready to call the original method
originalMethod.call( this, node, slide );
}
I think that would be the most non-intrusive way to do the override, without actually hacking in to the tree.jquery.js source.
See my custom.js at http://codepen.io/cellepo/pen/LGoaQx
The separate source tree.jquery.js is added externally in the JS settings of that codepen.
How can I get access (from within my custom.js file) to JqTreeWidget variable that is in the source file (tree.jquery.js)? Is it even possible? Is JqTreeWidget not in scope outside of tree.jquery.js, or is it not a global variable? I was hoping treeContainer.tree.prototype would have it, but I haven't had luck so far...
Thanks!
The prototype object can be obtained via:
jQuery.fn.tree("get_widget_class").prototype
Note that this is not a generalized solution for any jQuery plugin. This is something explicitly implemented by the tree plugin.
I found this hacky workaround. But since it's a hack, I'd still prefer to find the Answer as posed in this Question (so please, continue to Answer with respect to the <reference to JqTreeWidget.prototype.openNode> I mentioned in the Question, thanks)...
As stated in this Question, the goal involves making it possible to override JqTreeWidget.prototype.openNode (from tree.jquery.js) externally in custom.js. As such, calls to changeAncestorHeightRecursively (my code) & JqTreeWidget.prototype.openNode would both be made from the override in custom.js, and tree.jquery.js source is not modified at all.
Workaround:
Declare global var in html:
<script type='text/javascript' language="javascript">
changeAncestorHeightRecursively = 1;
</script>
In custom.js, set the globar var to the function (the one I want to be called before JqTreeWidget.prototype.openNode):
window.changeAncestorHeightRecursively = changeAncestorHeightRecursively;
Call the global-var-referenced function at the beginning of JqTreeWidget.prototype.openNode (hack into tree.jquery.js):
JqTreeWidget.prototype.openNode = function(node, slide) {
// Only way I could figure out to get this to execute before the rest of this method
// (global-var-referenced function in custom.js)
changeAncestorHeightRecursively( node, true );
// Rest of original openNode code...
}
This calls my code function from within tree.jquery.js, as opposed to calling the overridden method from within custom.js. So this is hacky because of the global var, and modifying tree.jquery.js source.
This will work for now, but hoping for a less hacky Solution as stated in this original Question... Thanks!
I have been writing a reusable script, let's call it a plugin although it's not jQuery, that can be initialised in a declarative way from the HTML. I have extremely simplified it to explain my question so let's say that if a user inserts a tag like:
<span data-color="green"></span>
the script will fire because the attribute data-color is found, changing the color accordingly.
This approach proved very handy because it avoids anyone using the plugin having to initialise it imperatively in their own scripts with something like:
var elem = document.getElementsByTagName('span')[0];
myPlugin.init(elem);
Moreover by going the declarative way I could get away without defining any global (in this case myPlugin), which seemed to be a nice side effect.
I simplified this situation in an example fiddle here, and as you can see a user can avoid writing any js, leaving the configuration to the HTML.
Current situation
The plugin is wrapped in a closure like so:
;(function(){
var changeColor = {
init : function(elem){
var bg = elem.getAttribute('data-color');
elem.style.background = bg;
}
};
// the plugin itslef looks for appropriate HTML elements
var elem = document.querySelectorAll('[data-color]')[0];
// it inits itself as soon as it is evaluated at page load
changeColor.init(elem);
})();
The page loads and the span gets the correct colour, so everything is fine.
The problem
What has come up lately, though, is the need to let the user re-evaluate/re-init the plugin when he needs to.
Let's say that in the first example the HTML is changed dynamically after the page is loaded, becoming:
<span data-color="purple"></span>
With the first fiddle there's no way to re-init the plugin, so I am now testing some solutions.
Possible solutions
Exposing a global
The most obvious is exposing a global. If we go this route the fiddle becomes
http://jsfiddle.net/gleezer/089om9z5/4/
where the only real difference is removing the selection of the element, leaving it to the user:
// we remove this line
// var elem = document.querySelectorAll('[data-color]')[0];
and adding something like (again, i am simplifying for the sake of the question):
window.changeColor = changeColor;
to the above code in order to expose the init method to be called from anywhere.
Although this works I am not satisfied with it. I am really looking for an alternative solution, as I don't want to lose the ease of use of the original approach and I don't want to force anyone using the script adding a new global to their projects.
Events
One solution I have found is leveraging events. By putting something like this in the plugin body:
elem.addEventListener('init', function() {
changeColor.init(elem);
}, false);
anybody will be able to just create an event an fire it accordingly. An example in this case:
var event = new CustomEvent('init', {});
span.dispatchEvent(event);
This would re-init the plugin whenever needed. A working fiddle is to be found here:
http://jsfiddle.net/gleezer/tgztjdzL/1/
The question (finally)
My question is: is there a cleaner/better way of handling this?
How can i let people using this plugin without the need of a global or having to initialise the script themselves the first time? Is event the best way or am I missing some more obvious/better solutions?
You can override Element.setAttribute to trigger your plugin:
var oldSetAttribute = Element.prototype.setAttribute;
Element.prototype.setAttribute = function(name, value) {
oldSetAttribute.call(this, name, value);
if (name === 'data-color') {
changeColor.init(this);
}
}
Pros:
User does not have to explicitly re-initialize the plugin. It will happen automatically when required.
Cons:
This will, of course, only work if the user changes data-color attributes using setAttribute, and not if they create new DOM elements using innerHTML or via some other approach.
Modifying host object prototypes is considered bad practice by many, and for good reasons. Use at your own risk.
I want to attach a jQuery plugin function to an element on my site that exists only on one page. Currently, I'm using this conditional to prevent triggering the limiter function and throwing an error when there is no #advertiser_co_desc in view.
jQuery(document).ready(function($) {
var elem = $('#charNum');
if ($('#advertiser_co_desc').length) {
$('#advertiser_co_desc').limiter(180, elem);
}
});
On my website #advertiser_co_desc is present only on one page.
My solution does the job, but my qualm stems from the fact that the jQuery plugin code as well as the plugin function call presented above (they are both in the same file) get fetched by the browser and the condition is continuously evaluated regardless of whether a user ever gets to a page where #advertiser_co_desc exists.
Is the method I'm using optimal, or is there a better way to attach this particular JS only to the page where '#advertiser_co_desc` exists? Naturally, I wan to avoid adding my scripts in the same file with the PHP code.
Or you can wrap the plugin method as,
var _limiter = $.fn.limiter;
$.fn.limiter = function(limit, element) { // provide complete argmuments
if(this.length) {
_limiter.call(limit, element);
}
};
Make sure that plugin is loaded before this statements.
The best and optimal way to check existence of an element by jquery, is $('#advertiser_co_desc').length that you have used already. So no need to change your code.
I need to call "MyOtherFunction" when "MyFunction"(which creates an element) completes, without MyFunction knowing what MyOtherFunction is.
The reason I need this is for extension of a jquery powered fileupload User Control that is used in several places with different functionality. A specific page shows a header and file count for it, and when the upload completes, I need to modify the file count according to how many files are displayed(by created elements) I thought :
$(UserControl).on(MyFunction, UploadElem, MyOtherFunction);
but this route is not accomplishing anything. The most I can alter the User Control is add in a function call, but without effecting the original user control functionality.
I'm not sure if because MyFunction isn't an event and doesn't bubble up or if it just isn't possible to use a defined function as a parameter of .on() is the reason I cannot get this code to work. Any suggestions?
Easiest way I can think of, is duck punching respectively hooking that method:
var _oldMyFunction = MyFunction;
MyFunction = function() {
_oldMyFunction.apply( this, arguments );
MyOtherFunction();
};
I managed to solve my own issue, but the context is important for the answer:
// Using a Global JavaScript object I created:
GlobalNameSpace.ExtensionFunction = function(oParam1, oParam2, oParam3)
{
/// <summary>All parameters are optional</summary>
return; // For instances when it is not being overwritten, simply return
}
//In the Code for the user control:
GlobalNameSpace.UploadControl.UploadComplete(oSender, oArgs)
{
///<summary>Handles the Upload process</summary>
// process the upload
GlobalNameSpace.ExtensionFunction(oSender, oArgs);
}
//and finally in the code to extend the functionality
GlobalNameSpace.Page.Init
{
///<summary>Initializes the page</summary>
// redefine the extension function
GlobalNameSpace.ExtensionFunction = function(oSender, oArgs)
{
GlobalNameSpace.Page.Function(oSender, oArgs);
}
}
This allows me to extend anything I need it to without polluting my objects, and having something generic already existing to call on to make my changes. This solution solves my problem of needing a onCreate function for the elements I create to represent my uploaded items to trigger the header displaying the number of files. Very useful
I need to be able to determine when an object is created (not a DOM element -- a JavaScript object).
An answer to this question has some very useful looking code for creating observable properties, so you can have a function fire when a property changes.
In my situation I need to do something when the object/property is created, not an existing property changed, and my limited understanding of such matters did not help me figure out if or how I could use that code to do this after much squinting.
The situation is: page loads a bunch of scripts. Some of the scripts create things that are needed by other scripts, e.g:
ThisStuff = (function () {
// blah blah
return self;
} ());
Some other code needs to initialize this ThisStuff, whenever it's available, which may be after the DOM is done loading. The user doesn't actually need ThisStuff right away, so it's fine for it to happen whenever the script is done loading. So I would like to do something along lines of:
$(document).ready(function() {
wheneverIsAvailable(window,'ThisStuff', function(object) {
object.init(args);
})
});
I realize there are other solutions to this problem (changing script order, or loading scripts on demand) but those are difficult because of the architecture. So I'm only interested in a way to do this versus other solutions. If jQuery offers some such functionality, that's fine also as I'm using it.
You could have a setInterval checking a number of times a second to watch the specific variable. You can check whether it is created using obj.hasOwnProperty(prop). When it is created, you invoke the function, and clear the interval.
It might be dirty but it might also just work fine for you.
Edit: I coded this for you: http://jsfiddle.net/jhXJ2/2/. It also supports passing additional arguments to the function.
window.__intervals = [];
function wheneverIsAvailable(obj, prop, func) {
var id = (Math.random()+"").substring(2);
var args = arguments;
window.__intervals[id] = window.setInterval(function() {
if(obj.hasOwnProperty(prop)) {
window.clearInterval(window.__intervals[id]);
func(Array.prototype.slice.call(args, 3));
// Call function with additional parameters passed
// after func (from index 3 and on)
}
}, 1000/ 50);
}
wheneverIsAvailable(window, 'test', function() {
alert(arguments[0]);
}, 'Woot!');
window.setTimeout('window.test = 123', 1000);
This is a bit far-fetched but it might work.
You would need to use knockoutjs, a javascript library. It's awesome but is built for a slightly different purpose.
Anyways it has a dependentObservable thing which allows to fire up an event whenever a certain value changes. Now I know you want on creation but you can check whether your variable holds any value (other than what you provided initially), if yes then consider it initialize.
Let me know if you think this sounds feasible.