I want to load a webpage in WebView but remove parts of the webpage. So, I created a custom WebViewClient. And, in onPageFinished(), I did some javascript to remove some elements. Then, I made the WebView visible.
However, when I run it, it sets the view visible, and then I see the elements being removed. It is as if the JS is running in the background very slowly. It creates a poor viewing experience because it flashes the full page and then the desired partial page.
Here is my onPageFinished()
#Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:"
+ "document.getElementsByClassName('header')[0].style.display='none';"
+ "document.getElementById('section_0').style.display='none';"
+ "document.getElementById('page-actions').style.display='none';"
+ "document.getElementsByClassName('languageSelector')[0].style.display='none';"
+ "document.getElementById('mw-mf-last-modified').style.display='none';"
+ "document.getElementById('footer').style.display='none';");
loadingView.setVisibility(View.INVISIBLE);
view.setVisibility(View.VISIBLE);
}
Any ideas on how to fix this?
In onPageFinished():
view.loadUrl("javascript:"
+ "var FunctionOne = function () {"
+ " var r = $.Deferred();"
+ " try{document.getElementsByClassName('header')[0].style.display='none';}catch(e){}"
+ " try{document.getElementById('section_0').style.display='none';}catch(e){}"
+ " try{document.getElementById('page-actions').style.display='none';}catch(e){}"
+ " try{document.getElementsByClassName('languageSelector')[0].style.display='none';}catch(e){}"
+ " try{document.getElementById('mw-mf-last-modified').style.display='none';}catch(e){}"
+ " try{document.getElementById('footer').style.display='none';}catch(e){}"
+ " setTimeout(function () {"
+ " r.resolve();"
+ " }, 2500);"
+ " return r;"
+ "};"
+ "var FunctionTwo = function () {"
+ " window.CallToAnAndroidFunction.setVisible();"
+ "};"
+ "FunctionOne().done(FunctionTwo);");
In MainActivity.onCreate():
this.webView.addJavascriptInterface(new JsObject(webView, loadingView), "CallToAnAndroidFunction");
In MainActivity():
public class JsObject {
private View loadingView;
private View view;
JsObject(View view, View loadingView){this.view = view;this.loadingView = loadingView;}
#JavascriptInterface
public void setVisible(){
runOnUiThread(new Runnable() {
#Override
public void run() {
view.setVisibility(View.VISIBLE);
loadingView.setVisibility(View.INVISIBLE);
}
});
}
}
So, it was a combination of making a JavascriptInterface and making a JS function to wait for the JS calls to finish before calling the interface (with the visibility settings).
You could try to speed up your WebView with:
webview.getSettings().setRenderPriority(RenderPriority.HIGH);
webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
Anyways, you shouldn't be making your WebView visible right away. Why don't you create an Interface (refer to http://developer.android.com/guide/webapps/webview.html, Binding JavaScript code to Android) and make a call from your javascript to:
public void myCallback(){ view.SetVisibilitu(View.VISIBLE) };
after the animations have ended?
Related
I'm building an app that uses a webview. It loads mobile version of a website. But my client wanted me to change and delete some text.
I did it with javascript but after i did this, some sliders stopped working anymore. Without javascript it works perfect. But i have to change and remove texts somehow.
my webview settings;
wSettings=webView.getSettings();
wSettings.setJavaScriptEnabled(true);
wSettings.setDomStorageEnabled(true);
webView.loadUrl("http://www.evdusum.com");
and the javascript that i used in onPageFinished;
#Override
public void onPageFinished(WebView view, String url) {
String link1 = "SİTEDE ARAYIN";
String link2 = "ARA";
String a="Bu sitede kullanılan yazılı ya da görsel dokümanlar izinsiz kullanılamaz.";
String b=" ";
view.loadUrl("javascript:(function(){document.body.innerHTML = document.body.innerHTML.replace('" + link1+"', '" + link2+"')})()");
view.loadUrl("javascript:(function(){document.body.innerHTML = document.body.innerHTML.replace('" + a+"', '" + b+"')})()");
((RelativeLayout)findViewById(R.id.start)).setVisibility(View.GONE);
}
Until Android api 18 (JELLY_BEAN_MR1) you can use this view.loadUrl("javascript:");. Just use ONE call to insert and call your js. If you need to perfome long code, build a function and call it at the "same call" of webView.loadUrl().
Some example:
webView.loadUrl("javascript:(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
"script.innerHTML = function your_function(var1, var2){document.body.innerHTML = document.body.innerHTML.replace(var1, var2); return true;}" +
"parent.appendChild(script);" +
"your_function('"+link1+"','"+link2+"')" + ";})();");
Android api >=19 use webView.evaluateJavascript. Here some example:
webView.evaluateJavascript("(function() {" +
"var parent = document.getElementsByTagName('head').item(0);" +
"var script = document.createElement('script');" +
"script.type = 'text/javascript';" +
"script.innerHTML = function your_function(var1, var2){document.body.innerHTML = document.body.innerHTML.replace(var1, var2); return true;}" +
"parent.appendChild(script);" +
"return your_function('"+link1+"','"+link2+"');})();", new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
Log.v("TAG", "Response from JS"+value)
}
});
I am trying to automate this webpage inside of Android. So far I got my webview to successfully fill out the form, but the only problem is the final button click at the bottom of the page. Here is my code:
// Fill out form
webview.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
WebView myWebView = (WebView) findViewById(R.id.webview);
//hide loading image
findViewById(R.id.progressBar).setVisibility(View.GONE);
//show WebView
findViewById(R.id.webview).setVisibility(View.VISIBLE);
myWebView.loadUrl("javascript:document.getElementById('join_first_name').value='" + name + "';void(0); ");
myWebView.loadUrl("javascript:document.getElementById('join_last_name').value='" + lastName + "';void(0); ");
myWebView.loadUrl("javascript:document.getElementById('join_email').value='" + email + "';void(0);");
myWebView.loadUrl("javascript:document.getElementById('join_confirm_email').value='" + email + "';void(0);");
myWebView.loadUrl("javascript:document.getElementById('join_password').value='" + password + "';void(0); ");
myWebView.loadUrl("javascript:document.getElementById('join_confirm_password').value='" + password + "';void(0); ");
myWebView.loadUrl("javascript:document.getElementById('phone_number_a').value='231';void(0); ");
myWebView.loadUrl("javascript:document.getElementById('phone_number_b').value='123';void(0); ");
myWebView.loadUrl("javascript:document.getElementById('phone_number_c').value='2310';void(0); ");
myWebView.loadUrl("javascript:document.getElementById('join_i_agree').click();void(0); ");
myWebView.loadUrl("javascript:document.getElementById('join_card_not_available').click();void(0); ");
myWebView.loadUrl("javascript:document.getElementById('#join-now-primary')[1];void(0); ");
myWebView.pageDown(true);
// Make sure a toast is only shown once.
while (toastCheck) {
Toast.makeText(MainActivity.this, "Click the \"join\" button.", Toast.LENGTH_SHORT).show();
toastCheck = false;
}
}
});
My Question is: How can I click the button at the bottom of the page?
HTML 4.01 specification says ID must be document-wide unique.
HTML 5 specification says the same thing but in other words. It says that ID must be unique in its home subtree, which is basically the document if we read the definition of it.
Source: https://softwareengineering.stackexchange.com/questions/127178/two-html-elements-with-same-id-attribute-how-bad-is-it-really
You should contact with website developer I guess. Also you can trigger it like this:
javascript:document.getElementById('join-now-primary').click();
Edit:
That website is using jQuery, so you can use jquery functions, please try again like this:
String injection = "javascript:"
injection += "$('#join_first_name').val('"+lastName+"');"
injection += "$('#join_email').val('"+email+"');"
injection += "$('#join_confirm_email').val('"+email+"');"
injection += "$('#join_password').val('"+password+"');"
injection += "$('#join_confirm_password').val('"+password+"');"
injection += "$('#phone_number_a').val('2122');"
injection += "$('#phone_number_b').val('122');"
injection += "$('#phone_number_c').val('122');"
injection += "$('#join_i_agree').trigger('click');"
injection += "$('#join_card_not_available').trigger('click');"
/* trigger form*/
injection += "var formElement = $('#join_now_form');"
injection += "formHandler = new FormHandler('#join_now_form', {disableDefaultSubmision: true});"
injection += "formHandler.clearError();"
injection += "formHandler.init();"
injection += "formHandler.submitHandler = function(){ formElement.trigger('submitHandler', [formHandler]);}"
injection += "AccountModals.init(formHandler, 'registration');"
webView.loadUrl(injection)
Problem: it is needed to send notification of an occurred event from external pivot.html hosted in iFrame to a Flex app for event handling.
I have a Flex app hosted in main.html . I also have pivot.html that is displayed in iFrame inside Flex app. Due to security domains I can send callback to Flex app ONLY from main.html . The problem is that I need to handle events that I receive on the component inside pivot.html in Flex app. So I would need to call main.html from pivot.html, and main.html will in turn call a function inside Flex app.
But how can I do it if I cannot call functions in main.html from pivot.html?
What needs to be done in pic:
Thanks
Answer on my own question (finally figured this out, hopefully it will help someone):
Define function to be dynamically injected
Injecter.as
...
public static var getIFrameWindow : String =
"document.insertScript = function ()" +
"{ " +
"getIFrameWindow = function(iFrameId){ " +
" var iframeWin;" +
" var iframeRef = document.getElementById(iFrameId);" +
" if (iframeRef.contentWindow) {" +
" iframeWin = iframeRef.contentWindow;" +
" } else if (iframeRef.contentDocument) {" +
" iframeWin = iframeRef.contentDocument.window;" +
" } else if (iframeRef.window) {" +
" iframeWin = iframeRef.window;" +
" }" +
" return iframeWin;" +
"}"+
"}";
public static var SETUP_CALLBACKS:String =
"document.insertScript = function ()" +
"{ " +
"var flexApp;" +
"setObjectID = function(value, iFrameId){" +
" var iframeWin = getIFrameWindow(iFrameId);" +
" iframeWin.addEventListener('pagechange', function pagechange(evt) {" +
" var page = evt.pageNumber; " +
" flexApp.currentPageChange(page);" +
" },false);" +
" flexApp = document.getElementById(value);" +
"}" +
"}";
...
Register these functions and inject them dynamically from Flex App to main.html using ExternalInterface
Viewer.mxml
...
ExternalInterface.call(Injecter.getIFrameWindow);
ExternalInterface.call("setObjectID", IFrame.applicationId, iFrame.getIFrameId());
ExternalInterface.addCallback("currentPageChange", currentPageChange);
...
pivot.html dispatch an event to get currentPageChange function called in Flex App
function SomeEventHandler()
{
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent("pagechange", true, true, "some_data_to_be_passed_here");
window.dispatchEvent(evt);
}
I think you can use postmessage communication.Try this https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
This question already has answers here:
Custom JSF component: Using "startElement" with "script" results in a comment
(2 answers)
Closed 7 years ago.
i try to put internal JS code into a JS block. This should be done by a custom Renderer for the h:commandButton. The Renderer is inbound and works finde. But as soon as i start to generate the script block it is put between <!-- and -->.
So here is the method for generating the script block:
private void writeScript(FacesContext ctx, HtmlCommandButton button, String event, String script) throws IOException {
String nonce = UUID.randomUUID().toString();
ResponseWriter writer = ctx.getResponseWriter();
ctx.setResponseWriter(writer);
writer.startElement("input", button);
writer.writeAttribute("type", "submit", null);
writer.endElement("input");
writer.startElement("script", button);
writer.writeAttribute("nonce", nonce, null);
writer.write("document.getElementById('" + button.getClientId()
+ "')."
+ event
+ " ="
+ " function() {"
+ script
+ "}");
writer.endElement("script");
}
Also using the startCDATA() method doesn't seem to work.
So instead of:
<script nonce="...">
document.getElementById('j_id1955899975_7494aa2f:submitButton').alert('hallo') = function() {onclick}
</script>
the generated code looks like:
<script nonce="...">
<!--
document.getElementById('j_id1955899975_7494aa2f:submitButton').alert('hallo') = function() {onclick}
//-->
</script>
What am I doing wrong ?
Would be really nice if someone could help me :D
According to Predrag Maric's link i changed code to the following:
private void writeScript(FacesContext ctx, HtmlCommandButton button, String event, String script) throws IOException {
System.out.println("das event: "+event);
String nonce = UUID.randomUUID().toString();
ResponseWriter writer = ctx.getResponseWriter();
ctx.setResponseWriter(writer);
writer.startElement("input", button);
writer.writeAttribute("type", "submit", null);
writer.writeAttribute("id", button.getClientId(), null);
writer.endElement("input");
writer.write("<script>");
writer.write("document.getElementById('" + button.getClientId()
+ "')."
+ event
+ " ="
+ " function() {"
+ script
+ "}");
writer.write("</script>");
}
The problem was writing the script tags with the writeElement() method of the ResponseWriter It automatically adds the comments... After coding it out it works, thx a lot !.
I try to get the height of the actual HTML content inside my WebView in order to set the height according to the content.
this is my script, but I get an empty string when the scripit is being invoked.
private async Task LoadHTMLContent(ItemViewModel itemVm)
{
var htmlScript = "<script>function getDocHeight() { " +
"return document.getElementById('pageWrapper').offsetHeight; } </script>";
var htmlConcat = string.Format("<html><head>{0}</head>" +
"<body style=\"margin:0;padding:0;\" " +
">" +
"<div id=\"pageWrapper\" style=\"width:100%;" +
"\">{1}</div></body></html>", htmlScript, itemVm.Model.Content);
webView.NavigationCompleted += webView_NavigationCompleted;
webView.NavigateToString(htmlConcat);
}
async void webView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
string pageContentHeight = await webView.InvokeScriptAsync("getDocHeight", null);
}
Well, I found a solution.
If you wish to do the same, feel welcome to check out my solution on GitHub:
https://github.com/romshiri/sizeable-webview