Load a javascript method from Android Webview - javascript

I am trying to render a chart in an Android WebView using HighCharts.
What my app does should be simple :
- Load the HTML page and external (but local) css and js files
- Load data from the Java part of the app
- Call a javascript function with the previously-loaded data as a parameter
I setup my webview by activating Javascript :
mWebView.getSettings().setJavaScriptEnabled(true);
And also by setting a WebClient to catch javascript's console messages :
mWebView.setWebChromeClient(new WebChromeClient() {
public boolean onConsoleMessage(ConsoleMessage cm) {
Log.d(TAG, cm.message() + " -- From line "
+ cm.lineNumber() + " of "
+ cm.sourceId() );
return true;
}
});
This is my HTML page :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="style.css" />
<title>Chart</title>
<script src="jquery.js"></script>
</head>
<body>
<script src="highstock.js"></script>
<script src="exporting.js"></script>
<script language="javascript">
function plot() {
console.log("Hello !");
}
</script>
<div id="container" style="height: 100%; width: 100%"></div>
</body>
</html>
I load this page from the app's assets by calling
mWebView.loadUrl("file:///android_asset/page.html");
My external CSS file seems to be read and I suppose the external JS files are also correctly loaded
I read everywhere that I can call my javascript method anytime by calling
mWebView.loadUrl("javascript:plot()");
However, I always get the error
Uncaught ReferenceError: plot is not defined -- From line 1 of null
Is there anything I might have forgotten ?
Note that I load my page and call the javascript method right after the loadUrl call.
Thanks !

Ok, I'm sorry for your time loss. I was looking for an answer for hours, and then within 15 minutes of posting the question I found the solution.
The problem seemed to be the two consecutive calls to loadUrl. Maybe the page wasn't properly loaded yet while I was already calling the javascript function.
I added a load listener on the WebView like this
mWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (url.equals("file:///android_asset/page.html")) {
mWebView.loadUrl("javascript:plot();");
}
}
});
so that I call the javascript method only when the page is completely loaded.
Now it works properly on every call.
Thank you again for your attention !

Try this:
<body>
<script src="highstock.js"></script>
<script src="exporting.js"></script>
<script language="javascript">
function plot() {
console.log("Hello !");
}
plot(); //Note the calling of the function within the HTML file once loaded into webView
</script>
<div id="container" style="height: 100%; width: 100%"></div>
</body>
And remove the line:
mWebView.loadUrl("javascript:plot()");
If you want to pass data to your javascript function, hen send your data through the URL like this:
mWebView.loadUrl("file:///android_asset/page.html?data=something");
and use:
var param1var = getQueryVariable("data");
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
alert('Query Variable ' + variable + ' not found');
}
in your JavaScript like what is done here. You can also check out this example.

Related

JavaScript does not work on pages other than my MasterPage and pages that aren't linked to it

Sorry for the bad english :P
I'm using ASP.NET
I'm trying to use a JavaScript that I've found on the Internet to set masks to my telephone and cellphone text fields, but the JS code just work at the fields that are located at my MasterPage. I don't have much experience with JS (almost none) and I have no idea how to resolve this since there is no error at my Logcat.
I tried to reference it at my MasterPage's head, body and inside the ContentPlaceHolder.
Here is the JS code:
function mascara(o, f) {
v_obj = o
v_fun = f
setTimeout("execmascara()", 1)
}
function execmascara() {
v_obj.value = v_fun(v_obj.value)
}
function mtel(v) {
v = v.replace(/\D/g, "");
v = v.replace(/^(\d{2})(\d)/g, "($1)$2");
v = v.replace(/(\d)(\d{4})$/, "$1-$2");
return v;
}
function id(el) {
return document.getElementById(el);
}
window.onload = function () {
id('txtTelefone').onkeypress = function () { //Located at MasterPage - working
mascara(this, mtel);
}
id('txtCelular').onkeypress = function () { //Located at MasterPage - working
mascara(this, mtel);
}
id('telefoneContato').onkeypress = function () { //Located at Contact Page - not working
mascara(this, mtel);
}
id('txtCelularUser').onkeypress = function () { //Located at User Page - not working
mascara(this, mtel);
}
id('txtTelefoneUser').onkeypress = function () { //Located at User Page - not working
mascara(this, mtel);
}
}
As I said before I tried to reference my JS file in some places, the codes that I tried were:
<script src="<%# Page.ResolveClientUrl("../Componentes/js/mascaras.js") %>"></script>
<script src="../Componentes/js/mascaras.js"></script>
As you can see, the fields are located in diferent pages, I also tried to put the code directly at the pages but I had no luck.
I think that I don't need to post my entire MasterPage here, then I'll put just the head, but if exists a need I will edit the post.
<head runat="server">
<title></title>
<link rel="shortcut icon" href="../imagens/icone.ico" type="image/x-icon" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="utf-8" />
<link href="../Componentes/bootstrap/css/bootstrap.min.css" rel="stylesheet" media="screen" />
<link href="../Componentes/fontawesome/css/font-awesome.min.css" rel="stylesheet" media="screen" />
<link href="../Componentes/css/MasterPage.css" rel="stylesheet" />
<link href="../Fontes/Fontes.css" rel="stylesheet" />
<script src="../Componentes/bootstrap/js/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/ScrollMagic.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/plugins/debug.addIndicators.min.js"></script>
<script src="../Componentes/bootstrap/js/bootstrap.min.js"></script>
<%--Mask Scripts--%>
<script src="<%# Page.ResolveClientUrl("../Componentes/js/mascaras.js") %>"></script>
<script src="../Componentes/js/mascaras.js"></script>
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
EDIT 1
Tried this:
id('<%= txtTelefoneContato.ClientID %>').onkeypress = function () { //Located at Contact Page - Still not working
mascara(this, mtel);
}
id('<%= txtCelularUser.ClientID %>').onkeypress = function () { //Located at User Page - Still not working
mascara(this, mtel);
}
id('<%= txtTelefoneUser.ClientID %>').onkeypress = function () { //Located at User Page- Still not working
mascara(this, mtel);
}
The reference to the script is at MasterPage head. But the problem still the same
EDIT2
Im receiving this when I inspect my page:
Error image 1
Error image 2
Resolved
As VDWWD explained, I just put the JS code directly at the .aspx pages with this modification:
from:,
id('txtCelularUser').onkeypress = function () {
mascara(this, mtel);
}
to:
id('<%= txtCelularUser.ClientID %>').onkeypress = function () {
mascara(this, mtel);
}
Thank you in advance
You have to use it like this when accessing elements with JavaScript that have master pages. And it's also better to use it even without master pages. If you should change the ID in you don't have to change it also in your script.
id('<%= txtTelefoneUser.ClientID %>').onkeypress
If you look at the HTML source you will see that the ID of the element has been changed from txtTelefoneUser to something like this ContentPlaceHolder_txtTelefoneUser. Aspnet adds a prefix to ensure every element has a unique ID.
You will see the same usage of prefix in elements that repeat data like GridView/Repeater/DataList etc.
UPDATE
If you put the javascript in an external file you can put a reference to it on the master page like this (assuming Componentes is a folder in the root):
<script src="/Componentes/js/mascaras.js" type="text/javascript"></script>
However putting <%= txtTelefoneUser.ClientID %> in an external file will not work, only on an .aspx page. To make that work declare those elements as variables an assign them on the aspx page while using them in the external js file.
Below a demo:
In the external javascript file:
var myElement;
function myFunction() {
myElement.value = "Success!";
}
and then on the .aspx page:
<script type="text/javascript">
myElement = document.getElementById("<%= TextBox1.ClientID %>");
</script>
After a long time I found that the elements that area inside the Content to be found in javascript must have the name of the content ID in front more undeline and the name of the element ID, you are looking for.
example:
document.querySelector("#ContentPlaceHolder1_img1").setAttribute("src", 123);

Calling a Specific Web Service Method Using Javascript or jQuery

I've successfully invoked a web service when loading a jsp page. The issue I'm facing is setting up a user to be able to press a button to invoke a different method on that same web service. The function "getSS()" is supposed to accomplish this, but I suppose that javascript doesn't directly read that type of invocation. I've found solutions that call up the web service and pass a parameter to it, but those are usually not calling up specific methods within the webservice.
Here is my code, what can I put in the "getSS()" function to accomplish this? I've already loaded jQuery, and the web service is written in java. Note, I already wrote the code to do all the work, I just need to know how to call that specific code. None of the other solutions seem to fit my needs.
<%--
Document : index
Created on : May 6, 2016, 9:39:44 AM
Author : mmarino
--%>
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script type="text/javascript">
function getSS(){
var e = document.getElementById("names");
var daString = e.options[e.selectedIndex].value;
try {
org.me.calculator.CalculatorWS_Service service = new org.me.calculator.CalculatorWS_Service();
org.me.calculator.CalculatorWS port = service.getCalculatorWSPort();
// TODO process result here
java.lang.String result = port.SS(daString);
document.getElementById('inputhere').innerHTML = "hi";
} catch (Exception ex) {
// TODO handle custom exceptions here
document.getElementById('inputhere').innerHTML = ex;
}
}
</script>
</head>
<body>
<h1>Hello World!</h1> <%-- start web service invocation --%><hr/>
<button value="hi" onclick="getSS()"> Click</button>
<select id="names">
<%
try {
org.me.calculator.CalculatorWS_Service service = new org.me.calculator.CalculatorWS_Service();
org.me.calculator.CalculatorWS port = service.getCalculatorWSPort();
// TODO process result here
java.lang.String result = port.getNames();
out.println(result);
} catch (Exception ex) {
// TODO handle custom exceptions here
}
%>
<%-- end web service invocation --%>
</select>
<hr/>
<div id="inputhere">Hi.</div>
</body>
So it seemed all I was missing was setting that java code to a variable and putting the proper opening and closing statements:
function doIt(){
<%-- start web service invocation --%>
var x = <%
try {
org.me.testbilling.TestBillingWS_Service service = new org.me.testbilling.TestBillingWS_Service();
org.me.testbilling.TestBillingWS port = service.getTestBillingWSPort();
// TODO initialize WS operation arguments here
java.lang.String name = "Marcel" ;
// TODO process result here
java.lang.String result = port.getSS(name);
out.println(result);
} catch (Exception ex) {
// TODO handle custom exceptions here
}
%>
<%-- end web service invocation --%>
alert(x);
}
</script>
Still not quite sure how to make that "java.lang.String name =" be set to a javascript variable.

WebBrowser not running javascript

I'm utilizing a System.Windows.Forms.WebBrowser (used in a separate .dll file) from my WPF application in order to print some HTML content in the background.
My problem is that the HTML elemnts are not being affected by the javascript in the page.
I know that there are no errors in the page because that if I copy its content after the DocumentCompleted event and paste it into a plain HTML file the script works.
My HTML template:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<script>
function loaded() {
document.getElementById("content").innerHTML = "after javascript";
}
</script>
</head>
<body onload="loaded()">
<h1 id="content">before javascript</h1> <!--<= This value does not change-->
</body>
</html>
I use this HTML as follows:
private void PrintTest()
{
string curDir = Directory.GetCurrentDirectory();
string fullPath = Path.Combine(curDir, "PrintDocs\\HTMLPage1.html");
string html = File.ReadAllText(fullPath);
HtmlPrint.Print(html);
}
public static class HtmlPrint
{
public static void Print(string html, string documentTitle = "")
{
var wb = new System.Windows.Forms.WebBrowser { DocumentText = html };
wb.DocumentCompleted += wb_DocumentCompleted;
}
private static void wb_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
var wb = ((System.Windows.Forms.WebBrowser)sender);
wb.Print();
wb.Dispose();
}
}
What am I missing here?
You forgot the Braces in onload:
<body onload="loaded()">
As mentioned in my comments you can call the InvokeScript() method from your C# code and pass in your js function name as which should do the trick.
wb.Document.InvokeScript("loaded");
I think you have to change onload="loaded" to onload="loaded()"

Play Framework 2.1 websockets in Chrome

I can't seem to get websocket communication to work in the Play Framework version 2.1.
I created a simple test that does nothing but send messages back and forth with a push of a button. All the code for it is below. But nothing shows up except for the button.
Has anybody seen this problem or can someone tell me what I may be doing wrong in the code below?
I am using the latest version of Chrome.
Here is my simple setup.
In Application.java
public static Result index() {
return ok(index.render());
}
public static WebSocket<String> sockHandler() {
return new WebSocket<String>() {
// called when the websocket is established
public void onReady(WebSocket.In<String> in,
WebSocket.Out<String> out) {
// register a callback for processing instream events
in.onMessage(new Callback<String>() {
public void invoke(String event) {
System.out.println(event);
}
});
// write out a greeting
out.write("I'm contacting you regarding your recent websocket.");
}
};
}
In Routes File
GET / controllers.Application.index()
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)
GET /greeter controllers.Application.sockHandler()
In Index.Scala.html
#main(null) {
<div class="greeting"></div>
<button class="send">Send</button>
<script type="text/javascript" charset="utf-8">
$(function() {
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var sock = new WS("#routes.Application.sockHandler()")
sock.onmessage = function(event) {
$('.greeting').append(event.data)
}
$('button.send').click(function() {
sock.send("I'm sending a message now.")
});
})
</script>
}
In Main.scala.html
#(title: String)(content: Html)
<!DOCTYPE html>
<html>
<head>
<title>#title</title>
<link rel="stylesheet" media="screen" href="#routes.Assets.at("stylesheets/main.css")">
<link rel="shortcut icon" type="image/png" href="#routes.Assets.at("images/favicon.png")">
<script src="#routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
</head>
<body>
#content
</body>
The problem is in
var sock = new WS("#routes.Application.sockHandler()")
you have to specify the protocol and the complete url in the format: ws://localhost:9000/greeter.
Check this question to do it in javascript: How to construct a WebSocket URI relative to the page URI?
you can use a Route's webSocketURL() method to retrieve a url that can be passed to a WebSocket's constructor. Here's an example from Play's websocket-chat sample code:
$(function() {
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
var chatSocket = new WS("#routes.Application.chat(username).webSocketURL()")
var sendMessage = function() {
chatSocket.send(JSON.stringify(
{text: $("#talk").val()}
))
$("#talk").val('')
}
// ...
So in your code you can use something like
var sock = new WS("#routes.Application.sockHandler().webSocketURL()");
Personally I don't like intermingling interpolated code with JS, since I think that any code executing on the client should only be concerned with the state of the client, and not the server (not to mention it makes refactoring the script out into an external file impossible), so I tend to do something like this:
<div class="container app-container"
data-ws-uri="#routes.Application.WSUri.webSocketURL()">
.......
</div>
Then in my JS I can just do something like
var sock = new WS(document.querySelector(".app-container").dataset.wsUri);
// ....

How to call Onload function in the Main page as well as #include file inner.jsp page

I have two pages one is the main page and the another one is the inner page:
Page names: main.jsp , sidebar.jsp
I want to call the onload function on both of these pages. Is that possible. If yes How?
Below is the code for main.jsp:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%# include file="/pages/common/init.jsp"%>
<%# taglib prefix="sx" uri="/struts-dojo-tags"%>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>J.C. Taylor - Broker Website</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link rel="stylesheet" href="/css/default.css" media="screen" type="text/css" />
</head>
<body onload="prepopulateFields();load(17);">
<s:form name="continue" id="continue_id" action="continue" method="POST" validate="true" enctype="multipart/form-data">
<div id="wrapper">
<div id="main">
<div id="sidebar">
<%# include file="/pages/common/sidebar.jsp"%>
<span class="clearIt"></span>
</div>
The sidebar.jsp is:
<body onload="setSelected();">
//Some static content here
</body>
So Basically I want is to call prepopulateFields() Javascript method which belongs to onload() event of the main .jsp page and setSelected() which belongs to onload() method of the sidebar.jsp symulatneously and separately.
I know I can call the setSelected(); method inside the prepopulateFields() method but that I dont want to do. All I want is when the page is loaded both the onload functions should be called separately.
If you have some suggestions please do let me know!
I know I am being little bit ridiculous here but if I could do that My job will be very easy.
i don't think you can call more than one onload function.
best way is to call the method from already called function
function prepopulateFields(){
if //condition which check the current page where you want other onload function
setSelected();
}
<body onload="prepopulateFields();load(17);">
</body>
You cannot nest HTML <body> elements, it would only malform HTML.
Best is to put it as a <script> at the bottom of sidebar.jsp.
<script type="text/javascript">setSelected()</script>
If you use firebug to inspect the rendered html page of main.jsp. You would see there is only one < body > element. The < body > element in your sidebar.jsp is not rendered since it will malform HTML as html or body not allowed in included jsp.
Be careful that the included file does not contain <html>, </html>, <body>, or </body> tags
The solution is:
either put your setSelected() into main.jsp body onload event if the sidebar.jsp is always loaded;
or do as BalusC suggested.
window.onload = codeAddress; should work. Here's a demo. And the full code:
<script type="text/javascript">
function codeAddress() {
alert('ok');
}
window.onload = codeAddress;
</script>
Put the class definition in the parent jsp, and instantiate as many onloads as you need in the includes.
<SCRIPT>
// this portion was placed above the Class definition for convenience.
// make sure the Class definition gets loaded first in your code.
new OnLoad(function(){
alert("document loaded");
},100);
</SCRIPT>
...
<SCRIPT>
// Class Definition
OnLoad = function(taskFunction,miliseconds) {
var context = this;
context.cnt = 0;
context.id = null;
context.doTask=taskFunction;
context.interval = function() {
if(document.readyState == "complete"){
try{ context.stop();} catch(e){ throw new Error("stop error: " + context.id); }
try{ context.doTask();} catch(e){ throw new Error("load error: " + context.id); }
}
};
context.start = function(timing) {
if(context.id && context.id!=null)
context.stop();
context.cnt=0;
context.id=setInterval(context.interval,timing);
};
context.stop = function() {
var _id = context.id;
clearInterval(context.id);
context.id=null;
};
context.start(miliseconds ? miliseconds : 100);
};
</SCRIPT>

Categories