Screen width as a condition to redirect to other url during on load
-I'm trying to do this for only specific html pages, but can't seem to unless I put the redirect function (given in the link) as a script within the specific HTML page I want to invoke this.
Here is a breakdown of what I have/need
I have 3 files (desktop.html, mobile.html, script.js).
I want the desktop.html to redirect (or load) automatically the mobile.html if the screen width is < 992px.
The code I want to use will be kept in a separate JS file which is called script.js, this file already has a bunch of named functions that are within a variable. All the named functions (so far) are triggered when the user clicks on a button that corresponds. But I wont have a button that 'triggers' for redirecting to mobile.html page, as it should be automatic if the screen width is < 992px
Example of JS file
var name = {
first: function() {
<---! Does something when a button is pushed on my site --->
},
second: function() {
<---! Does something when a different button is pushed on my site --->
},
window.onload = redirectMobileHandler();
window.onresize = () => redirectMobileHandler();
function redirectMobileHandler() {
const width = Math.max(document.clientWidth || 0, window.innerWidth || 0);
if(width < 992) {
window.location = 'https://linktoyourmobilesite.com';
}
}
};
The HTML files reference the JavaScript files, so you have two options to only trigger that logic for one of your three HTML files:
Only link to the JavaScript file containing the logic in the target HTML file, by creating a new .js file that contains that logic (say index.js):
index.html
<link rel='index.js'>
index.js
var name = {
...
}
Reference the same JavaScript file in all three HTML files, but only call that function from within the target HTML file:
index.html
<button onclick='indexOnly()'>Button</button>
main.js
function indexOnly() {
var name = {
...
}
}
The latter option is preferable, as you can then make use of a template / header to handle loading all JavaScript files in a single location.
There is also technically a third option (though it is really just a combination of the two). If you use a framework like Angular, React or Vue, you'll get the concept of components, which automatically split this logic out for you, using encapsulation.
If I understand you correctly, you want to include the script to the target html file dynamically?
If so, there are numerous ways to accomplish this, for example:
var script = document.createElement('script');
script.src = 'script.js';
document.body.appendChild(script);
You can find more examples here.
However, I am not exactly sure what exactly you want to achieve. Please clarify, if my assumption is wrong.
Related
I'm trying to call a function from my html: tasks-div-upload.html to my other html: task-tareas-actualizadas.html.
I'm including my scripts on the script tags of the html files
I tried to call the function like this
First of all this is the html that calls the function: tasks-divs-upload.html
and the function is in task-tareas-actualizadas.html
I tried to call the function like i do in java that is
writing the class and then the function, for example: people.countPeople(5);
In this case, there are not classes because its an html file so what can I do?
//tasks-divs-upload.html
function contadorTareas(){
for(var x = 0; x < divs; x++){
var numeroTareas = x;
}
prueba(numeroTareas); // <---------
}
//task-tareas-actualizadas.html
function prueba(numero){
console.log(numero);
}
Console shows this error "Uncaught ReferenceError: prueba is not defined"
This CAN be done but is mostly a bad idea and is not very common and has some specific requirements. It is best it NOT be done unless the user is aware of the interaction.
IF your task-tareas-actualizadas.html opens tasks-divs-upload.html in a new window then tasks-divs-upload.html can call window.opener.prueba() BUT, if the first window gets closed, it will not be there and they must both be of the same origin.
This interaction can also go the other way if the parent keeps a reference to the child window.
Better to create a JavaScript file say "myfunctions.js" that includes the functions you wish to use and include it in both pages UNLESS for some reason you need/want the pages to interact - say the child page alters the parent page DOM or some such.
Reference https://developer.mozilla.org/en-US/docs/Web/API/Window/opener
and https://developer.mozilla.org/en-US/docs/Web/API/Window/open
Well scripts in HTML are JavaScript code. They need to be either defined in separate .js files or included in html using <script> tags.
It is not possible to define a JavaScript function in a html file and then use it in another html file. You need to define the function is a separate JavaScript file and then include this file in the html page.
You may also use JavaScript modules which are natively supported by modern browsers.
An example is the easiest way to explain this one. I am working with code that has lots of javascript inside <script> tags on the actual page. I normally like to put javascript in external files (plus then it's better for refactoring when you find things in common across many pages). The difficulty is that the code if full of dynamic function names, class names and ids... like this:
function handlePosData#(mySuffix)(data) {
$('#myDiv#(mySuffix)').css('cursor', 'auto');
if (data && data.length > 0) {
$('#lstPos#(mySuffix)').data('kendoGrid').dataSource.data(data);
}
}
mySuffix is a GUID generated at the top of the the Razor code
(I am brand-new to this codebase, so don't ask me WHY it is like this. The web app can have many different popups open at once, and I am assuming this is a solution to the need to ensure unique names ... )
Any ideas how to enable keeping the same scheme, but with external javascript files?
I take it these functions are called from your razor page (rather than an external js file). If so you can do the following:
Change the function so it is standard and instead of using the razor directly in the function, use a js variable:
// this can be moved to external js
function handlePosData(data, mySuffix) { // pass in mySuffix to function so it is a js var
$('#myDiv' + mySuffix).css('cursor', 'auto');
if (data && data.length > 0) {
$('#lstPos' + mySuffix).data('kendoGrid').dataSource.data(data);
}
}
Then in your razor code, you just call the function like this:
handlePosData(data, '#(mySuffix)'); // not sure what your data is so just left that as a var that you pass in
I would like to use Plupload in an Angular2 component and access the Plupload JavaScript file from a CDN. I want it specific to a component so that it is not downloaded if it is not required - I want it to be in a lazy loaded module. How can I do this?
Now fully answered on this page!
The result of this quest, which included offering and awarding bounties to two people who worked hard with me on it, is as follows:
Example of using Plupload with Angular 2 and TypeScript
How to Lazy load a script from a CDN in Angular 2
Example of how to use Plupload in a lazy loaded module
How to use a lazy loaded script in Angular 2
(See edit history for the ugly details that used to make up this question.)
Here's the overview of what you need to do to create a lazy-loaded Plupload feature while loading Plupload from a CDN:
When the feature is needed (e.g. user clicks a button or visits a page), dynamically add a <script> tag to the page to load the Plupload library from a CDN.
Wait until the library is loaded to proceed (or you could get a "plupload is undefined" error).
Display the UI to interact with Plupload in one of your Angular templates. In its simplest form, this UI consists of two buttons: "Select files" and "Upload files".
Initialize Plupload and wire it up to the UI.
Complete, working code: https://plnkr.co/edit/4t39Rod4YNAOrHmZdxqc?p=preview
Please take note of the following points in my implementation:
Regarding #2. A better way to check whether Plupload has finished loading would be to poll the global namespace for the existence of the plupload variable. As long as window.plupload does not exist, it means the library hasn't been loaded yet and that we should NOT proceed. For simplicity my code just waits for one second and proceeds.
Number 4 can prove a bit tricky. Plupload makes a heavy use of direct DOM access to wire its API to the HTML (e.g. document.getElementById('filelist')). This is something Angular discourages and that you should try avoiding whenever possible. More specifically direct DOM access is used in the following places:
To tell Plupload which DOM element should trigger the "Select files" dialog (what they call the browse_button config option). For this I could not avoid the direct DOM reference and I used the #ViewChild decorator to get a hold of the "Select Files" button.
To display selected files in the template. For this I converted the Plupload syntax into the regular Angular syntax. I push selected files to a class property called fileList which I display in the template using a standard *ngFor.
The "Upload Files" button triggers some code that does the actual uploading and refreshes the UI to show upload progress. Once more, I converted this to regular Angular syntax using event binding and data binding.
Let me know if you have any questions.
In this approach no need for any extra loader modules.
See example (check console for Woohoo): http://plnkr.co/edit/gfUs4Uhe8kMGzPxwpBay?p=preview
updated plunker: https://plnkr.co/edit/leG062tg7uX8sLrA0i2i?p=preview
You can lazyload some js by adding the script url to you document:
Create a my-lazy-load.function.ts:
export function lazyload(url) {
// based on https://friendlybit.com/js/lazy-loading-asyncronous-javascript/
let scripts = document.getElementsByTagName('script');
for (let i = scripts.length; i--;) {
if (scripts[i].src.match(url)) return true;
}
let s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = url;
let x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
return true;
}
In your component that you want to add plupload:
import {lazyload} from "./my-lazy-load.function.ts";
export class MyComponent implements OnInit {
pluploadInterval:number = null;
hasPlupload: boolean = false;
ngOnInit() {
lazyload("https://cdnjs.cloudflare.com/ajax/libs/plupload/2.3.1/plupload.full.min.js");
this.pluploadInterval = window.setInterval(()=>{
if(window.plupload) { // you can check existence of plupload object any way you want
// Woohoo, I am ready to be used
this.hasPlupload = true; // everything is run outside ngZone, wrap it if angular2 is not reacting to changes, or change window.setInterval to setInterval
window.clearInterval(this.pluploadInterval); // don't forget to clean interval
}
}, 100); // timeinterval can vary
....
The browser will load this automatically.
Notice if(plupload) it assumes that there is global object plupload that the script adds (I do not know if it truely added, check your working example in pure javascript). As it is jquery extension you can check it's prototype like this: jQuery test for whether an object has a method?
OLD HISTORICAL:
#Reid here is plunker: https://plnkr.co/edit/zDWWQbTQUSHBqCsrUMUi?p=preview the plupload is actually loaded, but added to require with define("plupload", ['./moxie'], extract); I am not sure at the moment how to extract from there and which package require is belong to... the code for finding correct module loader belongs to plupload itself, here it is (from plupload.dev.js):
if (typeof define === "function" && define.amd) {
define("plupload", ['./moxie'], extract);
} else if (typeof module === "object" && module.exports) {
module.exports = extract(require('./moxie'));
} else {
global.plupload = extract(global.moxie);
}
I think that your best bet is to use the Require.js Library so that you can dynamically load your scripts from within your components.
The small trade off is that you will have to add this 18KB library to your index.html page (CDN), however this could save you huge amounts of loading if your 3rd party libraries are massive.
I have no experience with using plupload, so instead I put together the following plunkr which uses an external animation library, drawn from a CDN. The plunkr animates a number from 0 - 100.
https://plnkr.co/edit/fJCtezsERYHOYplLh7Jo?p=preview
index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.3/require.min.js"></script>
component.ts
ngOnInit(){
// Dynamically loads the framework from the CDN.
require(["https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenLite.min.js"],
// This function is then called ONCE LOAD IS COMPLETE
function (common) {
// Do greensock things
var demo = {score:0},
scoreDisplay = document.getElementById("scoreDisplay");
//create a tween that changes the value of the score property of the demo object from 0 to 100 over the course of 20 seconds.
var tween = TweenLite.to(demo, 20, {score:100, onUpdate:showScore})
//each time the tween updates this function will be called.
function showScore() {
scoreDisplay.innerHTML = demo.score.toFixed(2);
}
});
}
What I like about this approach, is that in the onLoad callback from require, the syntax is unchanged from a normal implementation of the library, so you can just copy paste your currently working code into the callback.
Please I have few html pages that i include some js scripts after the body . My script are working fine and successful. However, it gets messy putting the two together as the project grow. I then took the js in to an individual files . The problem is, now i reference all the files and the js codes became available for each and every html page.
I have events where I check each page load , because i want to execute or start my file on the page load . Now this page loads fire on every page load . Example below,
(function() {
$(this).on('load', function(){
console.log("the init is");
var g = true;
if(g === false)
window.location = '/';
init();
});
var init = function(){
$('#btnAdd').on('click',function(e){
e.preventDefault();
e.stopPropagation();
alert("Butoon click");
});
};
})();
Before when I embed a similar code above, it fires when my page is executed and my load became the entry point as I wanted . But now I moved them to separate html file and reference them which most have the same or similar functions. Now when i visit very html page, the onload method fires regardless , because they are all reference and available for each page.
Please is there any way I can refactor my code to separate every js file for a separate html page/url. How do I make jQuery or ajax load call with reference to the url? How do I make each file fires when its respective html/url is requested from server or loading? Any help would be appreciated
There are a couple common approaches to executing different page initialization code on different pages.
You can break the page-specific initialization code and supporting code into separate script files and only include them in <script> tags on the actual pages where they are needed.
You can use a common script file that is loaded across many pages and then within the page initialization code, you can check which page is loaded and then decide in your code which code is appropriate to run.
You can use a single common script file that includes a separate function for each separate page that needs unique initialization, but no code that calls those functions and then add one unique initialization function call to each individual page in the body of the page. This allows for the most efficient browser caching of script files and loading of fewer script files (it lets you combine and minimize most of your scripts).
For the second option, you can detect which page is loaded in your Javascript a number of different ways.
You can detect a class name on a known tag (like the body tag).
You can set a unique JS variable in each page that the initialization code can test.
You can look for the existence of certain HTML tags in the page that uniquely indicate which type of page it is. This works well when you want a particular initialization function to run on a bunch of pages that all have a certain common element.
Here's an example of option 1 (detect class name on body tag).
var m = document.body.className.match(/pageType_([^\s]+)/);
if (m) {
switch(m[1]) {
case "home":
// code here
break;
case "category":
// code here
break;
case "product":
// code here
break;
default:
// code here
break;
}
}
Here's an example of option 2 (set unique JS variable in each page):
<script>
// somewhere in each unique page
var pageType = "home";
</script>
<script>
// common script file
switch(pageType) {
case "home":
// code here
break;
case "category":
// code here
break;
case "product":
// code here
break;
default:
// code here
break;
}
</script>
Here's a scheme for detecting which unique tag exists and calling the appropriate initialization function:
if (document.getElementById("home")) {
initHome();
} else if (document.getElementById("category") {
initCategory();
}
I understand that for performance reasons it is better to let the asset pipeline concatenate and minify all my javascript and send the whole lot with every page request. That's fair enough
However, a bunch of my javascript is things like binding specific behaviours to specific page elements - stuff like
$('button').click(function(e) { $('input.sel').val(this.name); }
and I would feel more comfortable if I knew that this code was being executed only on that page - not on evey other page which might coincidentally have elements with the same IDs or which matched the same selectors How do people deal with this?
I would rather not put all this stuff inline in elements, just because when it gets to be more than about two lines long, keeping javascript correctly indented inside an .html.erb file is more work than it needs to be
Here is what I do (based on some stackoverflow answers):
application_helper.rb
def body_page_name
[controller_name.classify.pluralize, action_name.classify].join
end
application.html.haml
%body{data: {page: body_page_name}}
application.js
$(function() {
var page = $("body").data("page");
if("object" === typeof window[page])
window[page].init();
});
And in appropriate js file there's an object called ControllerAction:
tickets.js
var TicketsShow = new function() {
var self = this;
self.init = function() {
// code which may call other functions in self
};
};
There's probably better way to do it, but this works for me
I'll describe what I currently do, just in case it gives anyone a better idea
1) I changed the 'body' tag in my application.html.erb to add the current controller and action as data- attributes
<body data-controller="<%= controller.controller_name %>"
data-action="<%= controller.action_name %>" >
2) I test this at the top of the relevant javascript
$(document).ready(function() {
if($('body').data('controller')=='stories') {
$('.story').click(function(e) {
var u=$(this).data('url');
u && (document.location=u);
});
}
});
I can't decide if I think this is a good idea or not
For page specific JavaScript, I typically do something like this:
Application Helper
In the application helper I create a class attribute (though you could just as well use a data attribute instead).
module ApplicationHelper
def body_attributes
controller = params[:controller].gsub('/', ' ')
action = params[:action]
version = #version ? "version_#{#version}" : nil
{
class: ([controller, action, version] - [nil]).join(' ')
}
end
end
Note I'm also adding a version string. This helps with Google content experiments, and makes A/B testing a breeze.
Application.html.haml
In my global layout file, I do something like this to insert the attributes on the body tag:
!!! 5
%html
%head
...
%body{body_attributes}
script.js
Now in my page specific script, I just check for the class attributes, like this:
$(function () {
if ($('body.pledge.new, body.pledge.create').length > 0) {
// do work here...
}
});
The advantage of this method is that getting the body by class is very quick. The script inside the conditional will not be executed at all on any page apart than the ones I choose, so minimal overhead, and I don't need to change my selectors throughout the code.
EDIT
Note that this answer is now 3 years old. You should be using client-side routing with a framework like React instead.
I'd add a class to the BODY tag, allowing you to identify each page, and therefore each control per page.
<body class='page1'>
JS:
$('.page1 button').click(function(e) { $('input.sel').val(this.name); }
I've done it and seen it done in several different ways:
Rigging up the mvc to be able to load a particular js file per page, named along the same lines as a controller file. Like: <controller-name>.js
Making a url parser in JS and then setting a global variable to the current page: UrlParams.currentView = 'dashboard'; and then saying if(UrlParams.currentView == 'dashboard') { //do specific js here }
Setting a unique identifier as the page class or ID and then targeting that with your JS selectors. $('#dashboard').xyz();