jQuery - Using javascript variable elsewhere - javascript

I fear this question may be extremely newbie level, but I am just drawing a blank.
Within the $(document).ready function I have some DatePicker code...
$('#date-view1').datePicker({
selectWeek: true,
inline: true,
startDate: '01/01/1996'
}).bind('dateSelected', function (e, selectedDate, $td) {
$('#date1').val(selectedDate.asString());
var pfb = selectedDate.asString();
});
The part I am struggling with is the var pfb = selectedDate.asString();
I want to use the variable pfb further down my page in a different function called showProjects().
How can I do this? I have tried declaring the variable inside and outside of the $(document).ready function without luck.
Thanks

Declare var pfb before your document ready block. That'll make it available elsewhere on the page. In the document ready you'll be SETTING an already DECLARED variable.

In Javascript you can use global variables to store values which are accessible from anywhere in the page. Of the many ways of doing this is
setting the value using window.pfb = selectedDate.asString();
and accessing it later anywhere with window.pfb

I'm not sure if this is a problem area, but I wouldn't have tried passing pfb as a param in that onclick event - I think that may re-initialise pfb, or create a new var.
If you're creating it globally (not ideal but should work) then you shouldn't need to pass pfb as a param anyway.
Also, it's good practice not to attach events on the elements like that. Ideally - and jQuery makes this very easy - you should have something in your $(document).ready like this:
$(document).ready(function() {
$("#myButton").click(function() {
showProjects();
});
});
Or even shorten this to
$(document).ready(function() {
$("#myButton").click(showProjects());
});
if you know that showProjects() is the only call that you want to make.

It should work if you just drop the word var
Declaring variables without var makes them global.
It would probably be better form to declare it before the ready block as Dan Ray suggested, but you said you had a hard time with this? Not sure why you would.

Related

Overriding jQuery function loses 'this' scope

I am trying to implement the following answer from another question:
https://stackoverflow.com/a/26469105/2402594
Basically I need to add an extra check to a jQuery function. The following code is in jQuery library:
But I can't modify the original jQuery, so I am creating a patch in a separate file. What I am doing is overriding the find function and add functionality as follows:
(function() {
var originalFind = jQuery.fn.find;
jQuery.fn.find = function () {
try {
document === document;
}
catch (err) {
document = window.document;
}
return originalFind.apply(this, arguments);
};
})();
The function is overridden correctly, however, when the code calls 'find', my 'try' doesn't throw any exception when it should because the scope is different than the one inside the Sizzle function, so the original issue is still there.
I have also tried duplicating all of the Sizzle code, adding my modification and assigning it to jQuery.fn.find as done above, however the scope issue is still there and some crashes happen.
I need 'document' to be set before it reaches the following check or it crashes due to permission denied:
How could I share the scope so that the try/catch can be done correctly? Is it even possible? Any other ideas?
Thank you
As we all known, JavaScript has function scope: Each function creates a new scope. Scope determines the accessibility (visibility) of these variables. Variables defined inside a function are not accessible (visible) from outside the function.
So, if the document is defined in the JQuery library function, I think you can't access it. You could try to define a global variable to store the document, then, you can access it in the JQuery library function and the override function.
More details about Javascript Scope, check these links: link1 and link2

Access variable from 1 .js source file (jqTree), from within different .js file, to override method

(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!

Javascript / jQuery basic function issue

I am trying to display a div on click. The function that is supposed to make the magic happen is:
$(document).ready(function showGogoasa() {
$('.gogoasa-newsletter').show();
});
Unfortunately, it does nothing. Which makes me scratch my head for hours as I have done small things like this in the past and they worked. I am trying to make this modification on the website of a client.
When I check the firebug console it says the following: ReferenceError: showGogoasa is not defined
I tried looking on Google for this kind of error but the similar cases had this kind of issue for not declaring a variable. Well, I do not have any variables.
I am trying to display a div on click.
Your code is running the function on a ready event and doesn't give the error you describe.
Presumably (it would have helped if you had provided a complete test case) you are also trying to bind the function as a click handler, but you can't do that because you have defined it using a function expression and not a function declaration (so it doesn't create a variable called showGogoasa outside of its own scope).
Define the function separately, then assign call it and bind it as a click event handler on the ready event.
$(document).ready(function ready_handler() {
function showGogoasa() { // Define it as a variable in the current scope
$('.gogoasa-newsletter').show();
}
showGogoasa(); // call it now
$("button").on("click", showGogoasa); // call it then
});
Well, I do not have any variables.
That's the problem :)
Functions are first class objects and when you say showGogoasa() that means "Get the value of showGogoasa and call it as a function".
Using jsfiddle or providing more code would have been helpful.
One issue is that you are missing the click event handler. For example when the user clicks on X then Y should happen/show. The following simple example may help you to see how it works:
http://jsfiddle.net/fionaredmond/1vbagj12/
$(document).ready(function(){
$("#showGogoasa").click(function(){
$(".gogoasa-newsletter").show();
});
});
$(document).ready(function() {
$('#idOfYourClickerElement').on('click', function(){
$('.gogoasa-newsletter').show();
});
});

How to re-evaluate a script that doesn't expose any global in a declarative-style component

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.

How should I store my javascript variables?

I am currently coding in this way:
<script type="text/javascript">
var linkObj;
Is this a safe way to store data? My concern is what if a jQuery or other plug-in was to also use the variable linkObj. Also if I declare my variable like this then can it also be seen by other functions in scripts located in other js files that I include?
$(document).ready(function(){
var linkObj;
});
as long as you use the var keyword, any variable defined in that scope won't be accessible by other plugins.
I you declare a variable this way it will be accessible to all scripts running on the page.
If you just want to use it locally, wrap it in a function:
(function() {var linkObj; ... })()
However, this way nothing outside of the function will be able to access it.
If you want to explicitly share certain variables between different scripts, you could also use an object as a namespace:
var myProject = {}
myProject.linkObj = ...
This will minimize how many global names you have to rely on.
Wrap it in a closure:
<script type="text/javascript">
(function() {
var linkObj;
// Rest of your code
})();
</script>
This way no script outside your own will have access to linkObj.
Is this a safe way to store data?
This is not storing data per se, it's only declaring a variable in a script block in what I assume is an HTML page. When you reload the page in the future, it will not hold previous values.
My concern is what if a jQuery or other plug-in was to also use the variable linkObj.
That's a valid concern, like others have pointed out. However, you would expect plugins not to rely on scope outside the plug-in. This shouldn't impact a lot as good plug-in design would likely prevent this from happening.
Also if I declare my variable like this then can it also be seen by other functions in scripts located in other js files that I include?
Yes. As long as their execution is triggered after your script block gets loaded. This normally follows the order in which your script declaration appears in the page. Or regardless of the order they appear on the page if they are executed, for example, after the jQuery DOM 'ready' event.
It's common to hear that is good to avoid 'global namespace pollution', which relates to this concern. To accomplish that you can use a function to contain code, and directly invoke that function in your script block.
(function () {
var a = 1; // the scope is within the function
alert('The variable a is equal to: ' + a);
}) (); // the parenthesis invoke the function immediately

Categories