Android Webview Anchor Link (Jump link) not working - android

I have a WebView in my Android App that is loading an HTML string using the loadDataWithBaseURL() method. The problem is that local anchor links (<a href="#link">...) are not working correctly. When the link is clicked, it becomes highlighted, but does not scroll to the corresponding anchor.
This also does not work if I use the WebView's loadUrl() method to load a page that contains anchor links. However, if I load the same URL in the browser, the anchor links do work.
Is there any special handling required to get these to work for a WebView?
I am using API v4 (1.6).
There isn't much to the code, here are the relevant parts of some test code I've been working with:
WebView detailBody = (WebView) findViewById(R.id.article_detail_body);
String s = "LINK!<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><a name=\"link\"></a>Testing!";
detailBody.loadDataWithBaseURL(API.HomeURL(this), s, "text/html", "utf-8", "");

It looks like the problem is that I had a WebView within a ScrollView. The WebView isn't able to scroll to an anchor link when configured like this. After refactoring my layout to eliminate the ScrollView, the anchor links work correctly.

Android Webview Anchor Link (Jump link) Not Working
True, WebView Anchor Links, or Jump Links initiated through the #LINK extension to the URL will not work when the WebView is inside of a ScrollView(*).
Still, the problem for me and apparently others is that the #LINK does work when launched from a touch in an href, but is ignored when launched via the URL. Other symptoms include navigating to the link only on the first time in a session or navigating to the bottom of the html file.
The Solution is to load the url after a short delay.
Here is an example:
My html is saved in assets: res/assets/help.html
With anchors like this:
<a name="helplinkcontacts"/>
And loaded like this:
final String baseUrl = "file:///android_asset/help.html#helplinkcontacts";
final WebView helpTextView = (WebView)findViewById(R.id.help_dialog_text);
helpTextView.loadUrl(baseUrl); // Ignores Anchor!!
I added the timer like this:
final String baseUrl = "file:///android_asset/help.html#helplinkcontacts";
final WebView helpTextView = (WebView)findViewById(R.id.help_dialog_text);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
helpTextView.loadUrl(baseUrl);
}
}, 400);
Note: Shorter delays, such as 100ms failed to navigate to the link.
(*) It turns out that so many of us have our WebViews inside of ScrollViews because we started out with a TextView rendering Spannable text which both supports some HTML and requires a ScrollView. Anyways, remove the ScrollView as soon as you convert your TextView into a WebView.

My Solution is , Check this Answer
public class MainActivity extends Activity {
WebView myWebView;
public static boolean flag = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myWebView = new WebView(this);
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.loadUrl("file:///android_asset/chapters.html");
setContentView(myWebView);
myWebView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
if (url.contains("#") && flag == false) {
myWebView.loadUrl(url);
flag = true;
} else {
flag = false;
}
}
});
}
}

I had a similar problem. Nothing would jump to anchor tags in the html. I didn't have my WebView within a ScrollView. Instead the problem was the base url I passed into loadDataWithBaseURL did not have a colon (':') in it. I believe the baseUrl needs to have some text, then a colon, then some more text, for example "app:htmlPage24".
So here's the initial call to my WebView, just to load the data in the string HTML_24:
wv.loadDataWithBaseURL("app:htmlPage24", HTML_24, "text/html", "utf-8", null);
Then I have a list that jumps to sections on the screen, depending on the list item you tap:
sectionsLV.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
{
wv.loadUrl("app:htmlPage24#section" + arg2);
}
});
HTML_24 is something like:
<html>
...
<a name="section1"/>
...
<a name="section2"/>
...
<a name="section3"/>
...
</html>

I was also facing the same issue. Anchor link was jumping to arbitrary position. Make sure not to hide webview when loading.
Use Invisible instead of gone.
This change fixed my issue.

try this
String myTemplate = "LINK!<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><a name=\"link\"></a>Testing!";
myWebView.loadDataWithBaseURL(null, myTemplate, "text/html", "utf-8", null);
the word "Testing!" must be outside of the screen to see it works.

WebView in Android 4.0 fails to open URLs with links in them.
e.g.
"file:///android_asset/help.html#helplinkcontacts"
Here is how I got around it
WebView wv = (WebView) nagDialog.findViewById(R.id.wv);
wv.getSettings().setJavaScriptEnabled(true);
wv.setWebViewClient(new MyWebViewClient(link));
wv.loadUrl("file:///android_asset/help.html");
And define the custom WebViewClient class
class MyWebViewClient extends WebViewClient {
private String link;
public MyWebViewClient(String link) {
this.link = link;
}
#Override
public void onPageFinished(WebView view, String url) {
if (!"".equals(link) && link != null)
view.loadUrl("javascript:location.hash = '#" + link + "';");
}
}

Related

Set an onClick listerner for a button inside a WebView in Android

In my project I have a WebView in a fragment which loads a page from the internet. I want to set a listener on a button in that page so that when the user clicks on it I am able to move to a different fragment. I don't have the code for the webpage that is being loaded.
I was able to implement an onClick listener for the WebView itself by using this answer, but I can't think of a way to intercept the button press specifically inside the WebView.
Any suggestions on how to implement something like this? Thanks.
You can use WebViewClient.shouldOverrideUrlLoading to detect every URL changes on your WebViewClient and according to that you can open the particular fragment instead of setting click listener of a button in webview
You should set up WebViewClient for your WebView . Here is a simple snippet:
WebView webView;
webView.setWebViewClient(webClient);
WebViewClient webClient = new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url){
return true;
}
#Override
public void onLoadResource(WebView view, String url){
if( url.equals("http://cnn.com") ){
// Do the job here
}
}
}
Add a function to your button that calls alert("abc") in your web page.
Afterwards add following in your android code. The code will intercept your alert and you can implement your own code.
webView.setWebChromeClient(new WebChromeClient() {
public boolean onJsAlert(WebView view, String url, final String message, JsResult result) {
if (message.equals("abc"))
{
result.cancel();
return true;
}else
return false;
}
});
All the above answers were good workarounds, however, I ended up using this solution and followed the guide on Android Developers page to enable JavaScript Binding. Note that Android interface needs to be used in the JavaScript file in order for the interception to work.

WebViewClient not calling shouldOverrideUrlLoading

The problem is rather simple.
In the application we want to keep track of the current url being displayed. For that we use shouldOverrideUrlLoading callback from the WebViewClient by saving the url into a class field for every update. Here is the relevant code:
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
mCurrentUrl = url;
// If we don't return false then any redirect (like redirecting to the mobile
// version of the page) or any link click will open the web browser (like an
// implicit intent).
return false;
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
...
}
});
mWebView.loadUrl(mInitialUrl);
However, there is at least one scenario, where the callback never gets triggered and the mCurrentUrl field doesnt get updated.
The url: https://m.pandora.net/es-es/products/bracelets/556000
Last updated url (shouldOverrideUrlLoading never gets called when clicking the product): https://m.pandora.net/es-es/products/bracelets
I have tried with callbacks like onPageStarted(), but the url also gets filtered and there doesn't seem to be an accessible one upstream since its protected code.
Reading android documentation about WebView I found this:
https://developer.android.com/guide/webapps/migrating.html#URLs
The new WebView applies additional restrictions when requesting resources and resolving links that use a custom URL scheme. For example, if you implement callbacks such as shouldOverrideUrlLoading() or shouldInterceptRequest(), then WebView invokes them only for valid URLs.
But still doesnt make sense since the above url is generic and should meet the standard.
Any alternative or solution to this?
When you click a product on that web page, it loads the new content in with JavaScript and updates the visible URL in the address bar using the HTML5 History APIs.
From the above MDN article:
This will cause the URL bar to display http://mozilla.org/bar.html, but won't cause the browser to load bar.html or even check that bar.html exists.
These are sometimes called single-page applications. Since the actual loaded page doesn’t change, the WebView callback for page loads isn’t called.
In case you know precisely what kind of HTTP request you want to intercept, you could use the shouldInterceptRequest callback that gets called for each request. It’s likely that the web application loads some data from an API, for example when a product is shown, which you could then detect.
If detecting this isn’t possible, but you’re in control of the web application, you could use the Android JavaScript interface to invoke methods within the Android application directly from the web page.
If you’re not in control of the loaded page, you could still try to inject a local JavaScript file into the web page and observe when the history APIs are used, then call methods in your Android application over the JS interface. I tried observing these events in Chrome with the method described in the previous link and it seems to work fine.
Maybe this helps someone, although the signature in the question is correct, but Android Studio suggests the following method signature:
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
which then never called. It took me a while to notice that the right signature is:
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Sorry if this not 100% fit the question, but I believe this may help someone in the same situation. It's not always easy to notice that the second parameter is different.
Please omit mWebView.getSettings().setDomStorageEnabled(true);
Then again try, if a new url found then will invoke shouldOverrideUrl()
I had the same problem like you, and I've finished with extending of WebViewChromeClient with listening for callback to
public void onReceivedTitle(WebView view, String title)
mWebView.setWebChromeClient(mSWWebChromeClient);
private WebChromeClient mSWWebChromeClient = new WebChromeClient() {
#Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (!view.getUrl().equals(mCurrentUrl)) {
mCurrentUrl = view.getUrl();
//make something
}
}
};
For me the problem was below line -
mWebView.getSettings().setSupportMultipleWindows(true);
After removing it shouldOverrideUrlLoading was being called.
after stumbling on this problem and searching for solutions, I've found the one that worked perfectly for me
https://stackoverflow.com/a/56395424/10506087
override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
// your code here
super.doUpdateVisitedHistory(view, url, isReload)
}
Another approach you can try: Catch the url by javascript side. Initialize your webView with this:
webView.addJavascriptInterface(new WebAppInterface(getActivity()), "Android");
After page is completely loaded (You can use an algorithm to check this like this https://stackoverflow.com/a/6199854/4198633), then:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript("(function() {return window.location.href;})", new ValueCallback<String>() {
#Override
public void onReceiveValue(String url) {
//do your scheme with variable "url"
}
});
} else {
webView.loadUrl("javascript:Android.getURL(window.location.href);");
}
And declare your WebAppInterface:
public class WebAppInterface {
Activity mContext;
public WebAppInterface(Activity c) {
mContext = c;
}
#JavascriptInterface
public void getURL(final String url) {
mContext.runOnUiThread(new Runnable() {
#Override
public void run() {
//do your scheme with variable "url" in UIThread side. Over here you can call any method inside your activity/fragment
}
});
}
}
You can do something like that to get url, or anything else inside the page.
Add
webView.getSetting().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
then shouldOverrideUrl will be triggered.
onProgressChanged is always triggered when reloading, loading new page with userclick or XmlHttpRequest.
Compare the URL of previous load and the current load, you'll know it's reloading or loading a new page. This works perfect in my single page Web App.
First declare a global variable to store last URL.
String strLastUrl = null;
Then override onProgressChanged(WebView view, int progress)
mWebView.setWebChromeClient(new MyWebChromeClient(){
#Override
public void onProgressChanged(WebView view, int progress) {
if (progress == 100) {
//A fully loaded url will come here
String StrNewUrl = view.getUrl();
if(TextUtils.equals(StrNewUrl,strLastUrl)){
//same page was reloaded, not doing anything
}else{
//a new page was loaded,write this new url to variable
strLastUrl = StrNewUrl;
//do your work here
Log.d("TAG", "A new page or xhr loaded, the new url is : " + strLastUrl);
}
}
super.onProgressChanged(view, progress);
}
});
I've also tried above solutions, but most of them have issue in my case:
doUpdateVisitedHistory sometimes can not return correct url after "#" made by XmlHttpRequest.
My case is a single page web App. The web App uses javascript with
xhr to display new page when user click an item. For example, user is
currently at http://example.com/myapp/index.php , after clicking, the
browser url becomes
http://example.com/myapp/index.php#/myapp/query.php?info=1, but in
this case, doUpdateVisitedHistory returns
http://example.com/myapp//myapp/
onReceivedTitle doesn't work in my case because the response retrieved by XMLHttpRequest does not have <title></title> tag.
The JavascriptInterface method also works, but I'm afraid it will cause
security related issues with javascript.
public class AndroidMobileAppSampleActivity extends Activity {
/** Called when the activity is first created. */
String mCurrentUrl="";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView mWebView = (WebView) findViewById(R.id.mainWebView);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.setWebViewClient(new MyCustomWebViewClient());
mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
mWebView.loadUrl("https://m.pandora.net/es-es/products/bracelets/556000");
}
private class MyCustomWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
mCurrentUrl = url;
Log.i("mCurrentUrl",""+mCurrentUrl);
view.loadUrl(url);
return true;
}
}
}
try this one...

Android webview append css to existing site

I am using the webview as following
WebView webView = (WebView) this.findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("https://m.facebook.com");
It loads the page fine. Now I am trying to add a line head section of the page before it renders in the web view. e.g.
<style>.test{color:red}</style>
is it possible to add. if yes then how??
this can be done using evaluateJavascript
webView.evaluateJavascript("javascript: document.getElementById('header').setAttribute('style','background-color: red;') ", new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
//in case you have some return value from your script
}
});

How do I download HTML of a website periodically and use it in my webview android?

I am making an app that has to display the content of a website.
I created my website I want to use and it will be responsive so that it works with different devices.
The question is how do I download the html code of my website periodically (once a day) and display the downloaded html files in my webview.
the [page will contain only a simple table that I will regularly update online.
I would like if you could tell me the steps to achieve this?
Thank you in advance!
If it's not necessary to download the html code & display the updated, you can display the whole website by providing the url to webview, you only need to change the view of website for mobile devices & it will be reflected in the webview.
And if this is not the thing you want then you need to perform webservice call which in return provide you JSON String, parse it into local string & replace the html code.
public class Main extends Activity {
private WebView mWebview ;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWebview = new WebView(this);
mWebview.getSettings().setJavaScriptEnabled(true); // enable javascript
final Activity activity = this;
mWebview.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(activity, description, Toast.LENGTH_SHORT).show();
}
});
mWebview .loadUrl("http://www.google.com");
setContentView(mWebview );
}
}

Android WebView loading part of a website

Some websites have comments portion and many other unnecessary portions. I want to just display the main article excluding the other portions in my custom Android WebView.
I have tried the example from Display a part of the webpage on the webview android
But it still shows the full website.
There is also the loaddatawithbaseurl class in Android but it is not working too as you can't specify till which portion of a website to show using this class.
Is there any other way to do this?
Here it is with some changes...
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url)
{
String javaScript = "javascript: var form = document.getElementsByClassName('university_corner');"
+ "var body = document.getElementsByTagName('div');"
+ "body[0].innerHTML = form[0].innerHTML";
webView.loadUrl(javaScript);
}
});
webView.loadUrl(url);

Categories

Resources