So I have this simple page:
#page "/"
#inject IJSRuntime _jsRuntime
<button #onclick="MakeSound">Make sound</button>
<button #onclick="ShowPopUp">Show pop-up</button>
#code
{
protected override void OnInitialized()
{
_jsRuntime.InvokeVoidAsync("makeSound");
_jsRuntime.InvokeVoidAsync("showPopUp");
}
void MakeSound()
{
_jsRuntime.InvokeVoidAsync("makeSound");
}
void ShowPopUp()
{
_jsRuntime.InvokeVoidAsync("showPopUp");
}
}
in which I have 2 buttons: one for playing a sound and one for showing an alert.
They work fine but when I try to run these actions at initialization, only the alert message shows up and there's no sound. Why?
By the way, this is the JS, though I have a feeling that the problem is not here:
function makeSound() {
var audio = new Audio("sounds/ding.wav");
audio.play();
}
function showPopUp() {
alert("Hello!");
}
Calling JavaScript function in OnInitialized do not work, because DOM is not rendered yet, OnAftrRender is the right event to call JavaScript functions. So you can try like this
protected override void OnAfterRender(bool firstRender)
{
if(firstRender)
{
_jsRuntime.InvokeVoidAsync("makeSound");
_jsRuntime.InvokeVoidAsync("showPopUp");
}
}
Related
I am loading the webpage in webview using WebChromeClient(). The webpage has a dropdown whenever the user select's item from that dropdown I need to show a toast. For this, I'm following official doc I've implemented the same as the doc says. still, I'm getting the error in the console. "showToast is not a function".
In Fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val webSettings = webView.settings
webSettings.javaScriptEnabled = true
webSettings.domStorageEnabled = true
webSettings.databaseEnabled = true
webView.addJavascriptInterface(WebAppInterface(requireContext()), "Android")
webView.webChromeClient = object : WebChromeClient() {
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
Log.i(TAG, consoleMessage.message())
return true
}
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
if (newProgress == 100) {
if (webView != null) {
webView.settings.builtInZoomControls = true
webView.settings.displayZoomControls = false
webView.loadUrl("javascript:loadMobileDashboard($data);")
}
}
}
}
webView.loadUrl(url)
}
WebAppInterface:
class WebAppInterface(private val context: Context) {
private val TAG = WebAppInterface::class.java.simpleName
#JavascriptInterface
fun showToast(toast: String) {
Log.d(TAG, "showToast: $toast")
}
}
I tried several changes and searched about it on the internet didn't work single solution. Please let me know what mistake am making here. Thanks :)
Create an html page in your assets folder, let's say named dropDown.html.
Copy this code into that file
<input type="dropDown" value="Hello" onClick="showToastInWebView('strMsg')" />
<script type="text/javascript">
function showToastInWebView(toast) {
Android.showToast(toast);
}
</script>
Now load url like this
myWebView.loadUrl("file:///android_asset/dropDown.html");
Note: webView does not invoke the JS function you have added a bridge to, You need to use your own webpage(in this case which is dropDown.html) that does indeed invoke the function, either local(our case) or on the web.
As an example, let's assume for a project, I have a WebView instance I'm using in my main activity loading a local HTML file:
class MainActivity : AppCompatActivity() {
lateinit var myWebView:WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myWebView = findViewById(R.id.webview)
myWebView.getSettings().setAllowContentAccess(true);
myWebView.getSettings().setAllowFileAccess(true);
myWebView.settings.javaScriptEnabled = true;
myWebView.setWebChromeClient(WebChromeClient());
myWebView.loadUrl("file:///android_asset/websrc/mainPage.html")
myWebView.addJavascriptInterface(WebAppInterface(this), "Android")
}
}
class WebAppInterface(private val mContext: Context) {
#JavascriptInterface fun getStatus() {
MainActivity().myWebView.evaluateJavascript("setStatus('good to go!')", null)
//Or: MainActivity().myWebView.loadURL("javascript:setStatus('good to go!')")
}
}
I have a JavascriptInterface also, where from I would like to invoke Android.getStatus() in my HTML file:
<html>
<head>
...
</head>
<body>
<div id="status"></div>
</body>
<script>
document.addEventListener('DOMContentLoaded', function() {
Android.getStatus()
})
function setStatus(status) {
document.querySelector('#status').innerHTML = status
}
</script>
</html>
So far, I think understand why this wouldn't work. From what I've come to understand from answers like this or this, And especially in the documentation for WebView, I understand that the JavascriptInterface and the WebView are running on different threads, so something like setStatus() wouldn't be defined. So far, I've tried using runOnUiThread and Handler(). See an example for runOnUiThread:
#JavascriptInterface fun getStatus() {
MainActivity().runOnUiThread(
object : Runnable {
override fun run() {
MainActivity().myWebView.evaluateJavascript("setStatus('good to go!')", null)
//Or: MainActivity().myWebView.loadURL("javascript:setStatus('good to go!')")
}
}
)
}
This (and use of Handler), however, gives me: E/AndroidRuntime: FATAL EXCEPTION: JavaBridge and lateinit property myWebView has not been initialized. I really don't understand why. Should mention I'm new to Kotlin and Android.
Any help?
All I needed was to pass myWebView to the JavaScript interface along with this and use Runnable...
Creating the JS interface:
myWebView.addJavascriptInterface(WebAppInterface(this, myWebView), "Android")
And then inside a #JavascriptInterface function:
myWebView.post(Runnable {
myWebView.loadUrl("javascript:customFunction()")
})
I have a WinForm application that using cefsharp web browser. From what I learnt about cefsharp, the events MouseDown, Click cannot trigger in WinForm. So I decided to use javascript to send a message to the console, and trigger ChromiumWebBrowser.ConsoleMessage event instead. But that did not work well. Did I mess up the javascript code or execute the code wrongly?
Here are the codes, please noted that UI.cs is a Form object. A panel object pn_Browser already created in the design view. What I tried to do is, click any element and send "success" to the console. Then the Background show the received message if the message in console.log is "success".
Background.cs
public ChromiumWebBrowser browser;
public Background()
{
browser = new ChromiumWebBrowser("https://www.google.com");
browser.ConsoleMessage += browser_ConsoleMessage;
browser.FrameLoadEnd += browser_FrameLoadEnd;
}
public void browser_ConsoleMessage(object sender, ConsoleMessageEventArgs e)
{
if (e.Message == "success")
{
System.Windows.Forms.MessageBox.Show("Received message.");
}
}
public void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)
{
Chromium.ExecuteScriptAsync("document.addEventListener(\"click\", function() {alert('Detect a click.');});");
}
UI.cs
public UI()
{
InitializeComponent();
Background background = new Background();
pn_Browser.Controls.Add(background.browser);
background.browser.JavascriptMessageReceived += browser_JavascriptMessageReceived;
background.browser.ExecuteScriptAsyncWhenPageLoaded(
"function test() {console.log(\"success\")}" +
"document.addEventListener(\"click\", test);"
);
}
delegate void JavascriptReceivedCallback(object sender, JavascriptMessageReceivedEventArgs e);
private void browser_JavascriptMessageReceived(object sender, JavascriptMessageReceivedEventArgs e)
{
if (InvokeRequired)
{
JavascriptReceivedCallback callback = new JavascriptReceivedCallback(Browser_JavascriptMessageReceived);
Invoke(callback, new object[] { sender, e };
}
else
{
MessageBox.Show(e.Message.ToString());
}
}
OK so this works on Android... I'm stuck on iOS.
I have a custom HTML WebView which has a JavaScript event:
const string html= #"<button type=""button"" onClick=CSharp.SendButton(' + buttonCode + ')"">Click here</button>";
On Android, I have a webview custom renderer:
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (Control != null)
{
Control.Settings.JavaScriptEnabled = true;
Control.AddJavascriptInterface(new JSBridge(Forms.Context), "CSharp");
}
base.OnElementPropertyChanged(sender, e);
}
And JSBridge is a bridge between Javascript and C#:
public class JSBridge : Java.Lang.Object
{
Context context;
public JSBridge(Context context)
{
this.context = context;
}
[Export]
[JavascriptInterface]
public void SendButton(string code)
{
// I can pass the buttonCode here
}
}
How can I do something similar on iOS?
Please note: I am aware of the HybridWebView examples on the Microsoft website: I dont want to use a seperate Javascript file, I want to fire the event from my HTML and catch it in my renderer.
I've for example this link /Site/Tris/tris.aspx?sessionId=8a657a7b15ee44c39063c8ae45a6ed3b
This is the js code:
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var proxy = $.connection.myHub;
proxy.client.test = function () {
$('input').hide();//for testing
};
// start connetion
$.connection.hub.start(function () {
var sessionId = $(document).getUrlParam("sessionId");
proxy.server.joinGroup(sessionId);
});
});
</script>
This is the hub:
public void JoinGroup(string groupName)
{
this.Groups.Add(Context.ConnectionId, groupName);
}
public void test(string x)
{
Clients.Group(x).test();
}
This is the aspx code when I do a step:
protected void Image_Click(object sender, ImageClickEventArgs e)
{
string s = "8a657a7b15ee44c39063c8ae45a6ed3b";
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.Group(s).test();
}
When I call context.Clients.Group(s).test(); in js goes returns into the $.connection.hub.start and rejoin in the group adn test() is not called!
Why? How could I do?
Thanks
The reason was that when I click on an image and there is an event in ASPX, it sends a POST request so the page is refreshed right after
proxy.client.test = function () {
$('input').hide();//for testing
};`
is called.
I've to do all of this using only js avoid the POST request and the refresh.