I have two web page(a.php & b.php). They have very similar logic but distinct UI. I wrote two javascript.
They both look like:
aUI = {
displayMessage = function ...
showDetails = function ...
}
function foo() {
aUI.displayMessage();
aUI.showDetails();
// and other things about aUI.displayMessage() and aUI.showDetails()...
}
foo();
aUI.displayMessage() is different from bUI.displayMessage(). But a.js and b.js have the same foo().
I extracted foo(). So now I have three .js: aUI.js, bUI.js and logic.js.
logic.js:
function foo() {
UI.displayMessage();
UI.showDetails();
//other things about UI
}
foo();
aUI.js and bUI.js:
UI = {
displayMessage = function ...
showDetail = function ...
}
How can a.php know it should use aUI.js? I wrote the plain implement:
<script type="text/javascript" src="aUI.js"></script>
<script type="text/javascript" src="logic.js"></script>
It works but seems not clever. I have duplicated namespace 'UI' in a project.
Is there a better way?
What about this?
aUI.js and bUI.js have there own namespace like aUI and bUI.
And add some more code like this:
<script type="text/javascript" src="aUI.js"></script>
<script type="text/javascript">
var UI = aUI;
</script>
<script type="text/javascript" src="logic.js"></script>
This approach resolves the problem about duplicated namespace 'UI'. I think this is kind of DI.
This sounds like a classic problem to be solved by inheritance. You can do this any number of ways in javascript. Here are a few examples.
Classical inheritence: http://www.crockford.com/javascript/inheritance.html
Prototypal inheritence: http://javascript.crockford.com/prototypal.html
dojo.declare: http://docs.dojocampus.org/dojo/declare *
If you did this in Dojo, for example, it would look like this
ui-base.js
dojo.declare("_UI", null, {
displayMessage: function() { },
showDetails: function() { },
foo: function() {
this.displayMessage();
this.showDetail();
}
});
ui-a.js
dojo.declare("UI", _UI, {
displayMessage: function() { /* Override and define specific behavior here */ },
showDetails: function() { /* Override and define specific behavior here */ }
});
ui-b.js
dojo.declare("UI", _UI, {
displayMessage: function() { /* Override and define specific behavior here */ },
showDetails: function() { /* Override and define specific behavior here */ }
});
Then, in your PHP, you just include the appropriate javascript files
a.php
<script src="ui-base.js"></script>
<script src="ui-a.js"></script>
b.php
<script src="ui-base.js"></script>
<script src="ui-b.js"></script>
* The world has too many jQuery examples to make yet another, so you get Dojo this time around ;)
The solution is to architect your code that you don't need to do this and duplicate code.
But if you want to stick with it then you can create a file called logic.php and inside do something like this
header("Cache-Control: no-cache");
$currentScript = reset(explode(".", end(explode("/", $_SERVER["SCRIPT_NAME"]))));
read_file("scripts/" . $currentScript . ".js");
echo "\n;\n";
read_file("scripts/logic.js");
and in html
<script type="text/javascript" src="logic.php"></script>
this way the script will change it's content because it will concatenate the required script based on it's name and the content of logic.js. The downside of this is thatit invalidates the caching of the browser.
Another solution will be to synchronously load in logic.js the other module you need. You can get the name of the script from document.location.href
Related
I wanted to call a function defined in a first.js file in second.js file. Both files are defined in an HTML file like:
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
I want to call fn1() defined in first.js in second.js. From my searches answers were if first.js is defined first it is possible, but from my tests I haven't found any way to do that.
Here is my code:
second.js
document.getElementById("btn").onclick = function() {
fn1();
}
first.js
function fn1() {
alert("external fn clicked");
}
A function cannot be called unless it was defined in the same file or one loaded before the attempt to call it.
A function cannot be called unless it is in the same or greater scope then the one trying to call it.
You declare function fn1 in first.js, and then in second you can just have fn1();
1.js:
function fn1 () {
alert();
}
2.js:
fn1();
index.html :
<script type="text/javascript" src="1.js"></script>
<script type="text/javascript" src="2.js"></script>
You could consider using the es6 import export syntax. In file 1;
export function f1() {...}
And then in file 2;
import { f1 } from "./file1.js";
f1();
Please note that this only works if you're using <script src="./file2.js" type="module">
You will not need two script tags if you do it this way. You simply need the main script, and you can import all your other stuff there.
1st JS:
function fn(){
alert("Hello! Uncle Namaste...Chalo Kaaam ki Baat p Aate h...");
}
2nd JS:
$.getscript("url or name of 1st Js File",function(){
fn();
});
You can make the function a global variable in first.js
and have a look at closure and do not put it in document.ready put it outside
you can use ajax too
$.ajax({
url: "url to script",
dataType: "script",
success: success
});
same way you can use jquery getScript
$.getScript( "ajax/test.js" )
.done(function( script, textStatus ) {
console.log( textStatus );
})
.fail(function( jqxhr, settings, exception ) {
$( "div.log" ).text( "Triggered ajaxError handler." );
});
declare function in global scope with window
first.js
window.fn1 = function fn1() {
alert("external fn clicked");
}
second.js
document.getElementById("btn").onclick = function() {
fn1();
}
include like this
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
use "var" while creating a function, then you can access that from another file. make sure both files are well connected to your project and can access each other.
file_1.js
var firstLetterUppercase = function(str) {
str = str.toLowerCase().replace(/\b[a-z]/g, function(letter) {
return letter.toUpperCase();
});
return str;
}
accessing this function/variable from file_2.js file
firstLetterUppercase("gobinda");
output => Gobinda
It should work like this:
1.js
function fn1() {
document.getElementById("result").innerHTML += "fn1 gets called";
}
2.js
function clickedTheButton() {
fn1();
}
index.html
<html>
<head>
</head>
<body>
<button onclick="clickedTheButton()">Click me</button>
<script type="text/javascript" src="1.js"></script>
<script type="text/javascript" src="2.js"></script>
</body>
</html>
output
Try this CodePen snippet: link .
Please note this only works if the
<script>
tags are in the body and NOT in the head.
So
<head>
...
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
</head>
=> unknown function fn1()
Fails and
<body>
...
<script type="text/javascript" src="first.js"></script>
<script type="text/javascript" src="second.js"></script>
</body>
works.
This is actually coming very late, but I thought I should share,
in index.html
<script type="text/javascript" src="1.js"></script>
<script type="text/javascript" src="2.js"></script>
in 1.js
fn1 = function() {
alert("external fn clicked");
}
in 2.js
fn1()
Use cache if your server allows it to improve speed.
var extern =(url)=> { // load extern javascript
let scr = $.extend({}, {
dataType: 'script',
cache: true,
url: url
});
return $.ajax(scr);
}
function ext(file, func) {
extern(file).done(func); // calls a function from an extern javascript file
}
And then use it like this:
ext('somefile.js',()=>
myFunc(args)
);
Optionally, make a prototype of it to have it more flexible. So that you don't have to define the file every time, if you call a function or if you want to fetch code from multiple files.
first.js
function first() { alert("first"); }
Second.js
var imported = document.createElement("script");
imported.src = "other js/first.js"; //saved in "other js" folder
document.getElementsByTagName("head")[0].appendChild(imported);
function second() { alert("Second");}
index.html
<HTML>
<HEAD>
<SCRIPT SRC="second.js"></SCRIPT>
</HEAD>
<BODY>
method in second js<br/>
method in firstjs ("included" by the first)
</BODY>
</HTML>
window.onload = function(){
document.getElementById("btn").onclick = function(){
fn1();
}
// this should work, It calls when all js files loaded, No matter what position you have written
});
// module.js
export function hello() {
return "Hello";
}
// main.js
import {hello} from 'module'; // or './module'
let val = hello(); // val is "Hello";
reference from https://hype.codes/how-include-js-file-another-js-file
My idea is let two JavaScript call function through DOM.
The way to do it is simple ...
We just need to define hidden js_ipc html tag.
After the callee register click from the hidden js_ipc tag, then
The caller can dispatch the click event to trigger callee.
And the argument is save in the event that you want to pass.
When we need to use above way ?
Sometime, the two javascript code is very complicated to integrate and so many async code there. And different code use different framework but you still need to have a simple way to integrate them together.
So, in that case, it is not easy to do it.
In my project's implementation, I meet this case and it is very complicated to integrate. And finally I found out that we can let two javascript call each other through DOM.
I demonstrate this way in this git code. you can get it through this way. (Or read it from https://github.com/milochen0418/javascript-ipc-demo)
git clone https://github.com/milochen0418/javascript-ipc-demo
cd javascript-ipc-demo
git checkout 5f75d44530b4145ca2b06105c6aac28b764f066e
Anywhere, Here, I try to explain by the following simple case. I hope that this way can help you to integrate two different javascript code easier than before there is no any JavaScript library to support communication between two javascript file that made by different team.
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>
<body>
<div id="js_ipc" style="display:none;"></div>
<div id="test_btn" class="btn">
<a><p>click to test</p></a>
</div>
</body>
<script src="js/callee.js"></script>
<script src="js/caller.js"></script>
</html>
And the code
css/style.css
.btn {
background-color:grey;
cursor:pointer;
display:inline-block;
}
js/caller.js
function caller_add_of_ipc(num1, num2) {
var e = new Event("click");
e.arguments = arguments;
document.getElementById("js_ipc").dispatchEvent(e);
}
document.getElementById("test_btn").addEventListener('click', function(e) {
console.log("click to invoke caller of IPC");
caller_add_of_ipc(33, 22);
});
js/callee.js
document.getElementById("js_ipc").addEventListener('click', (e)=>{
callee_add_of_ipc(e.arguments);
});
function callee_add_of_ipc(arguments) {
let num1 = arguments[0];
let num2 = arguments[1];
console.log("This is callee of IPC -- inner-communication process");
console.log( "num1 + num2 = " + (num1 + num2));
}
better late than never
(function (window) {const helper = { fetchApi: function () { return "oke"}
if (typeof define === 'function' && define.amd) {
define(function () { return helper; });
}
else if (typeof module === 'object' && module.exports) {
module.exports = helper;
}
else {
window.helper = helper;
}
}(window))
index html
<script src="helper.js"></script>
<script src="test.js"></script>
in test.js file
helper.fetchApi()
I have had same problem. I have had defined functions inside jquery document ready function.
$(document).ready(function() {
function xyz()
{
//some code
}
});
And this function xyz() I have called in another file. This doesn't working :) You have to defined function above document ready.
TLDR: Load Global Function Files first, Then Load Event Handlers
Whenever you are accessing an element within a JS file or <script> block, it is essential to check to make sure that element exists, i.e., jQuery's $(document).ready() or plain JS's document.addEventListener('DOMContentLoaded', function(event)....
However, the accepted solution does NOT work in the event that you add an event listener for the DOMContentLoaded, which you can easily observe from the comments.
Procedure for Loading Global Function Files First
The solution is as follows:
Separate the logic of your JS script files so that each file only contains event listeners or global, independent functions.
Load the JS script files with the global, independent functions first.
Load the JS script files with event listeners second. Unlike the other previous files, make sure to wrap your code in document.addEventListener('DOMContentLoaded', function(event) {...}). or document.Ready().
Can I have a script only component in Polymer to hold all my helper functions used across application? I am not sure what is the recommended way of having reusable functions, constants that can be imported across components ?
<dom-module id="helper-functions">
<script>
(function() {
var helper1 = function() { ... };
var helper2 = function() { ... };
Polymer({
is : 'helper-functions'
});
})();
</script>
</dom-module>
You "could" do this, but it depends on what those helper functions are doing and whether they need any "Polymer" features.
One way to package up this sort of thing is as a "behavior", and seems to be the way that the Polymer Elements themselves are doing things. Split your helpers into functional areas and make each a separate behavior, and then include the behavior in those elements that need it. Here's an example to show how its done (I am including all my behaviors in the PAS namespace.
<link rel="import" href="../polymer/polymer.html">
<script>
window.PAS = window.PAS || {};
(function() {
'use strict';
var dialogs = [];
PAS.DialogBehaviour = {
attached: function() {
dialogs.push(this); //Capture all dialogs as they start up
},
_requestClose: function() {
this.async(function() { //Wait a second to allow inflight ajax to have a chance to finish
dialogs.forEach(function(dialog) {
if (dialog !== this) {
dialog._forceClose();
}
},this);
},1000);
},
_forceClose: function() {
//abstract
}
};
})();
</script>
I then include it in my elements like ...
Polymer({
is: 'pas-standin',
behaviors: [AKC.Route,PAS.DialogBehaviour,Polymer.NeonAnimatableBehavior],
listeners: {
'pas-error': '_closeDialog'
},
But for pure javascript functions, I have added my helper functions in my app.js file. I don't have that many at the moment, and I suspect if I did it would be a sign that I haven't designed the right elements.
I am using jQuery and YUI-3.6.0 in my application. I am using YUI history module. I trie to create a wrapper on YUI history as below. (This is saved in "history-wrapper.js")
var MyHistory;
(function() {
YUI().use('history', function (Y) {
var history = null;
MyHistory = function() {
history = new Y.History();
};
MyHistory.prototype.save= function() {
history.add({
data : "some data"
});
};
MyHistory.prototype.load= function() {
var data = (history.get("data");
//process data
};
});
})();
I am using this wrapper with following lines of code
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="yui/yui/yui-min.js"></script>
<script type="text/javascript" src="js/history-wrapper.js"></script>
<script type="text/javascript">
var jQ = jQuery.noConflict();
jQ(document).ready(function() {
var history = new MyHistory();
history.save();
});
</script>
I am using same code in two different application.
In one application, everything works fine as Wrapper is loaded first.
In other application it throws "MyHistory is not defined" as "jQ(document).ready" function is called before wrapper is loaded.
I have no idea what causes this behavior. Can anyone help me?
The callback function "function(Y){... where you define MyHistory...}" in the YUI.use() is only executed when the dependendies (history) are loaded.
Then, you have no assurance MyHistory is defined when you try to use your wrapper.
The solution may be you put your JQuery code in the the YUI.use() too. You can make the YUI loader load jquery and the history module, you the no longer need to wrap the Histroy plugin.
I'm not sure it is exactly like this (cannot check it now).
<script type="text/javascript" src="yui/yui/yui-min.js"></script>
YUI({
modules: {
'jquery': {
path: 'js/jquery.js'
}
}
}).use('history', 'jquery' , function (Y) {
var jQ = Y.jQuery.noConflict(); //or juste jQuery.noConflict();
jQ(document).ready(function() { //it is likely the document will already be ready
var history = new Y.History();
history.save();
});
});
To keep organized, I'd like to keep all the javascript for my site in a single file:
scripts.js
However, some of my scripts are only used on on some pages, other scripts are only used on other pages.
In my document-ready function it looks like this:
function home_page() {
// image rotator with "global" variables I only need on the home page
}
$('#form')... // jQuery form validation on another page
The problem with this, is that I am getting javascript to execute on pages it's not even needed. I know there is a better way to organize this but I'm not sure where to start...
One thing you could do would be to use classes on the <html> or <body> tag to establish the type of each page. The JavaScript code could then use fairly cheap .is() tests before deciding to apply groups of behaviors.
if ($('body').is('.catalog-page')) {
// ... apply behaviors needed only by "catalog" pages ...
}
Even in IE6 and 7, making even a few dozen tests like that won't cause performance problems.
I usually do something like this, or some variation (a little pseudo code below) :
var site = {
home: {
init: function() {
var self=this; //for some reference later, used quite often
$('somebutton').on('click', do_some_other_function);
var externalFile=self.myAjax('http://google.com');
},
myAjax: function(url) {
return $.getJSON(url);
}
},
about: {
init: function() {
var self=this;
$('aboutElement').fadeIn(300, function() {
self.popup('This is all about me!');
});
},
popup: function(msg) {
alert(msg);
}
}
};
$(function() {
switch($('body').attr('class')) {
case 'home':
site.home.init();
break;
case 'about':
site.about.init();
break;
default:
site.error.init(); //or just home etc. depends on the site
}
});
I ususally have an init() function that goes something like this:
function init() {
if($('#someElement').length>1) {
runSomeInitFunction()
}
... more of the same for other elements ...
}
Basically just check to see if the element exists on the page, if it does, run its own initialization function, if not, skip it.
The whole JS codes is cached by the browser after the first page load anyway, so there's no point in fragmenting your JS file down into page-specific pieces. That just makes it a maintenance nightmare.
You could use for each page object literals to get different scopes.
var home = {
other: function() {
},
init: function() {
}
};
var about = {
sendButton: function(e) {
},
other: function() {
},
init: function() {
}
}
var pagesToLoad = [home, about];
pagesToLoad.foreach(function(page) {
page.init();
});
I'm writing a simple jQuery plugin, but I'm having trouble being able to use multiple instances on a page.
For instance, here is a sample plugin to illustrate my point:
(function($) {
$.fn.samplePlugin = function(options) {
if (typeof foo != 'undefined')
{
alert('Already defined!');
} else {
var foo = 'bar';
}
};
})(jQuery);
And then if I do this:
$(document).ready(function(){
$('#myDiv').samplePlugin({}); // does nothing
$('#myDiv2').samplePlugion({}); // alerts "Already defined!"
});
This is obviously an over-simplified example to get across the point. So my question is, how do I have two separate instances of the plugin? I'd like to be able to use it across multiple instances on the same page.
I'm guessing that part of the problem might be with defining the variables in a global scope. How can I define them unique to that instance of the plugin then?
Thank you for your guidance!
I have the very same problem but i find a very handy solution i´ll post it for someone who may have this problem
when you define your variables insinde the plugin you could use the .data() to store all the variables you define
like this
(function($) {
$.fn.samplePlugin = function(options) {
var base = this;
this.foo // define foo
// do stuff with foo and other variables
// Add a reverse reference to the DOM object
this.data("pluginname", base);
};})(jQuery);
And when you want to use the same foo variable you should retrive the reference with this:
base = this.data("pluginname");
base.foo
Hope it helps
Logan
html:
<code class="resize1">resize1</code>
<code class="resize2">resize2</code>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('.resize1').ratiofix({message:'resize1'});
$('.resize2').ratiofix({message:'resize2'});
});
</script>
I have found 2 solutions - the first one is jquery widget factory
http://jqueryui.com/widget/
js code:
$.widget("custom.ratiofix",{
options:{
message:"nothing"
},
_create:function (){
var self=this;
this.setListeners();
},
setListeners:function (){
var self=this;
$(window).on('resize',$.proxy(self.printMsg,self));
},
printMsg:function (){
console.log(this.options.message);
}
});
And the second (without widget factory):
(function ($){
var Ratiofix = {
init: function(options, elem) {
this.options = $.extend({},this.options,options);
this.elem = elem;
this.$elem = $(elem);
this.setListeners();
return this;
},
options: {
message: "No message"
},
printMsg: function(){
console.log(this.options.message);
},
setListeners:function (){
var self=this;
this.$elem.on('click',function (){
console.log(self.options.message);
});
$(window).on('resize',$.proxy(self.printMsg, self));
}
};
$.fn.ratiofix=function (options){
this.init= function(options, elem) {
this.options = $.extend({},this.options,options);
this.elem = elem;
this.$elem = $(elem);
return this;
};
if ( this.length ) {
return this.each(function(){
var ratiofix = Object.create(Ratiofix);
ratiofix.init(options, this);
$.data(this, 'ratiofix', ratiofix);
});
}
};
})(jQuery);
In both cases plugins work separately and have own settings. In my case - 2 widgets listen to window resize and print to console own options.message
I'm not sure what you mean by having more than one instance of a plugin. A plugin would be available to use on any element.
This comment doesn't clarify much for me:
So say that it was a plugin that took
a "color" parameter and turned the
object into that color. Well, in that
case you'd need multiple instances, as
you're dealing with more than one page
element turning more than one color.
In this case, you would pass in different colors are arguments as needed:
$('div#foo').makeColor('red');
$('div#bar').makeColor('blue');
Each time you call the plugin, it will use whatever arguments you give it. The plugin isn't a class that needs instances.
Just throwing my solution in here:
(function ($){
$.fn.plugin = function (options){
var settings = $.extend({}, $.fn.plugin.defaults, options);
settings.that = $(this);
$.fn.plugin.init (settings);
};
$.fn.plugin.defaults = { objval: 'default' };
$.fn.plugin.init = function (settings){
settings.that.val (settings.objval);
};
}( jQuery ));
$('#target1').plugin ({objval: 'not default'});
$('#target2').plugin ();
DEMO
The settings variable is isolated every time you initialize the object.
To answer your question directly, you can use jQuery.noconflict() to avoid namespace collisions and thus potentially have multiple instantiations on a page..
var $j = jQuery.noConflict();
// Use jQuery via $j(...)
$j(document).ready(function() {
// etc
check here
But I question your design. Why are you writing a plugin that appears to not operate on a jQuery wrapped set ? .. Plugins should be written to assume they are operating on a jQuery array held in 'this'. In which case any state can be stored in each of the items being acted upon... But maybe you are building something different?
Please review this page
instead of writing this
$("#divid1").samplePlugin();
$("#divid2").samplePlugin();
you can do this way
$.plugin('samplePlugin1', samplePlugin);
$("#divid1").samplePlugin1();
$.plugin('samplePlugin2', samplePlugin);
$("#divid2").samplePlugin2();
You can have much details from here
http://alexsexton.com/?p=51
You need to use this.foo instead of var foo, so that the variable is only related to the current object.
This worked a treat for me! I had specific parameters for which pages/places I wanted to run a plugin and was able to achieve success by using a simple if statement. Hope this helps someone!
<!-- Begin JQuery Plugin Foo -->
<script src="js/foo_fun.js"></script>
<?php
if(substr_count(strtolower($currentUrl),"member")>0)
{
?>
<script>
$(document).ready(function(){
$('#vscroller').vscroller({newsfeed:'news_employee.xml', speed:1000,stay:2000,cache:false});
});
</script>
<?php
}
else
{
?>
<script>
$(document).ready(function(){
$('#vscroller').vscroller({newsfeed:'news_company.xml', speed:1000,stay:2000,cache:false});
});
</script>
<?php
}
?>
<!-- End JQuery Foo-->
I had the same problem : how to use many instances of a plugin on only one form ?
The usual way fails because in fact, the instance is not an instance of the plugin : it is an instance of jQuery.
So, if more than one element is defined to be managed by a plugin, each definition overrides the previous parameters.
It was necessary to have a look on the problem from another side.
A plugin is usually made to react on a specific event for a specific element. e.g.. onclick on a button, or when the mouse is over the element.
In my case, I had to use an autocomplete plugin for a city field, but my form has 5 tabs and in total 4 fields for the cities for 4 different parts of the information to be collected.
For each fields, parameters are specifics.
By the way, I've realised iI don't need to have the plugin active everytime : just on the appropriate event on the field is enough.
So I had an idea : an event manager for each element. When the event appends, so I define the plugin action.
Some code will be more efficient to explain : imagine you have 3 div blocks and your plugin must change the colours, but with specifics colours depending on which div is affected.
$(document).ready(function(){
// Wich elements are affected by the plugin
var ids = ['myDiv1','myDiv2','myDiv3'];
// foe each one :
for (v in ids)
{
//define from an event :
$('#'+ ids[v]).focus(function()
{
// depending which id is active :
var aParams, idDiv = $(this).attr('id');
// Choosing the right params
switch(idDiv)
{
case 'myDiv1':
aParams = {'color': '#660000', 'background-color': '#0000ff'};
break;
case 'myDiv2':
aParams = {'color': '#006600', 'background-color': '#ff00ff'};
break;
case 'myDiv3':
aParams = {'color': '#000066', 'background-color': '#ff0000'};
break;
default:
aParams = {'color': '#000000', 'background-color': '#ffffff'};
};
// Defining the plugin on the right element with the right params
$(this).myPlugin(
{
colors: aParams
});
});
}
});
And this works fine.
Sorry if my English is not perfect - I hope you understand well.