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
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)
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 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?
I am working on a mvc3 application and I need to modify the style of the ValidatioSummary message, to do so, I created my own HTM helper which is as follows:
public static MvcHtmlString MyValidationSummary(this HtmlHelper helper){
string retainHtml +="";
int counterror = 0;
if (helper.ViewData.ModelState..IsValid)
{
TagBuilder tag = new TagBuilder("div");
tag.Attributes.Add("class", "validation-summary-valid");
tag.Attributes.Add("data-valmsg-summary", "true");
tag.InnerHtml += "<span> There was" + countererror + "errors found<ul><li></li></ul>"
retainHtml += tag.ToString();
return MvcHtmlString.Create(retainHtml);
}
if (!helper.ViewData.ModelState.IsValid)
{
TagBuilder tag = new TagBuilder("div");
tag.Attributes.Add("class", "validation-summary-errors");
tag.Attributes.Add("data-valmsg-summary", "true");
retainHtml +="<div class='validation-summary-errors'><span>";
counterror = 1;
string temretainhtml ="";
foreach (var key in helper.ViewData.ModelState.keys)
}
foreach (var err in helper.ViewData.ModelState[key].Errors)
temretainhtml += "<li>Error " + countererror++ + " : " + err.ErrorMessage + "</li>";
}
retainHtml += "Error ! there was " + --countererror + " errors found";
retainHtml += "</span>";
retainHtml += "<ul>";
retainHtml += temretainhtml;
retainHtml += "</ul></div>";
}
return MvcHtmlString.Create(retainHtml);
}
}
}
This works perfect with server side validation, but I need to implement this style on the Client side validation as well, right now, the forms are displaying the validationSummary on the top of the page on client side but with the default MVC format, not the one I specified in my HTML helper, I've been doing a lot of research but unfortunately I haven't had any luck, may I need to do any change in the jquery.validate.unobtrusive.js file to apply these changes? or do I need to create another validation file in jquery? My experience in jquery is very poor and I am very lost right now, any help you can give me please it'll be really appreciated.
Many thanks!!!
Late answer:
The jquery.validate.unobtrusive.js is hard to hook in to. Instead of modifying the file (never ideal) try this:
Listen to the following event
form.bind("invalid-form.validate", handler...
And then then build your own summary:
form.bind("invalid-form.validate", function (evt, validator) {
var container = $(this).find("[data-valmsg-summary=true]");
var list = container.find("ul");
if (list && list.length && validator.errorList.length) {
list.empty();
container.addClass("validation-summary-errors").removeClass("validation-summary-valid");
$.each(validator.errorList, function () {
$("<li style='color:#00aa00'/>").html(this.message).appendTo(list);
});
}
});
You should have a look at the end of this tutorial:
http://thepursuitofalife.com/asp-net-mvc-3-unobtrusive-javascript-validation-with-custom-validators/