I want to execute JavaScript code by using CefSharp in Windows Forms, but it does not work. The code is as following, and the message test is not shown. Did I miss something?
var browser = new ChromiumWebBrowser("http://localhost:50056/simple.aspx");
browser.Name = "Simple Page";
browser.Dock = DockStyle.Fill;
this.Controls.Add(browser);
browser.ExecuteScriptAsync("alert('test');");
You must wait for the browser to have sufficiently loaded before executing JavaScript. It's tempting to start trying to access the DOM in OnFrameLoadStart, whilst the V8Context will have been created and you will be able to execute a script the DOM will not have finished loading. If you need to access the DOM at its earliest possible point, subscribe to DOMContentLoaded.
Some examples of executing JavaScript are below.
browser.RenderProcessMessageHandler = new RenderProcessMessageHandler();
public class RenderProcessMessageHandler : IRenderProcessMessageHandler
{
// Wait for the underlying JavaScript Context to be created. This is only called for the main frame.
// If the page has no JavaScript, no context will be created.
void IRenderProcessMessageHandler.OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame)
{
const string script = "document.addEventListener('DOMContentLoaded', function(){ alert('DomLoaded'); });";
frame.ExecuteJavaScriptAsync(script);
}
}
//Wait for the page to finish loading (all resources will have been loaded, rendering is likely still happening)
browser.LoadingStateChanged += (sender, args) =>
{
//Wait for the Page to finish loading
if (args.IsLoading == false)
{
browser.ExecuteJavaScriptAsync("alert('All Resources Have Loaded');");
}
}
//Wait for the MainFrame to finish loading
browser.FrameLoadEnd += (sender, args) =>
{
//Wait for the MainFrame to finish loading
if(args.Frame.IsMain)
{
args.Frame.ExecuteJavaScriptAsync("alert('MainFrame finished loading');");
}
};
I think, in the case of calling a JavaScript function that exists inside HTML, and passing input arguments, one can simply use the Browser.LoadingStateChanged event in the MainWindow constructor to make sure loading is initiated. This event will be called after the Browser_Loaded, where the HTML file is declared. Following is an example of the code:
public MainWindow()
{
InitializeComponent();
//Wait for the page to finish loading (all resources will have been loaded, rendering is likely still happening)
Browser.LoadingStateChanged += (sender, args) =>
{
//Wait for the Page to finish loading
if (args.IsLoading == false)
{
Browser.ExecuteScriptAsync("JavaScripFunctionName1", new object[] { arg1, arg2});
}
};
}
private void Browser_Loaded(object sender, RoutedEventArgs e)
{
Browser.LoadHtml(File.ReadAllText(GetFilePath("YourHTMLFileName.html")));
}
However, if you want to execute the JavaScript code and get results, you should use:
var result = await Browser.EvaluateScriptAsync("JavaScripFunctionName2", new object[] { });
MessageBox.Show(result.Result.ToString());
In HTML:
<html>
<body>
<script>
function JavaScripFunctionName1(arg1, arg2)
{
// something here
}
function JavaScripFunctionName2()
{
// something here
return result;
}
</script>
</body>
</html>
Related
Hello i have the next question, ive got the next function =
protected void lnk_Click( object sender, EventArgs e )
{
LinkButton btn = sender as LinkButton;
string text = btn.CommandName;
ScriptManager.RegisterStartupScript( this, GetType(), "script", "alert('"+ text + "');", true );
}
I want to run the function after a second or 1.5 secs because this is running before the page renders visually, causing a "visual bug" on which the li tags (for example) dont get the css properties.
Any suggestion would help, thanks!
The JavaScript content should run on the event DOMContentLoaded like this:
document.addEventListener("DOMContentLoaded", function(){alert('text');});
If you're sure you want to use the "dirty" way, use setTimeout:
setTimeout(function(){alert('text');}, 1500); // 1500 milliseconds
In async you can wait using Task.Delay
private async Task<Response> ExecuteTask(Request request)
{
var response = await GetResponse();
switch(response.Status)
{
case ResponseStatus.Pending:
await Task.Delay(TimeSpan.FromSeconds(2))
response = await ExecuteTask(request);
break;
}
return response;
}
I need the js script to stop and wait until a certain element appears on the page.
Can this be done with promises?
The fact is that the portal on which the script is used dynamically loads pages without going to the URL, and the necessary part of the script is to be executed on a specific page
Instead of stoping execution, you can move the code into into its own function and execute only after the element appears in DOM.
var check = setInterval("checkElem", 200);
function checkClear() {
clearInterval(check);
}
function checkElem() {
const el1 = document.getElementById("element1");
if (el1 !== null) {
console.log("executing");
yourFunction();
checkClear();
} else {
console.log("element does NOT exist");
}
}
function yourFunction() {
//your executable code
return "some-value";
}
I have a content script which modifies the DOM based on the current url.
My background script listens to chrome.webNavigation.onHistoryStateUpdated and sends a message to the content script with the current url:
chrome.webNavigation.onHistoryStateUpdated.addListener(onNavigation, { url: ... });
async function onNavigation(details) {
const { url, tabId } = details;
chrome.tabs.sendMessage(tabId, { id: 'my-msg-id', url });
}
Then, based on the url, the content script injects a certain div to the current page:
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
if (message.id === 'my-msg-id' && message.url === '...') {
await injectDiv();
}
...
});
My problem is that sometimes, the same div gets injected to the DOM twice.
This looks like a race condition that happens:
When the page loads slowly.
When the user navigates quickly between urls or goes back and forth between pages.
What I've tried:
Wrapping the injection logic with a guard like this:
async function injectDiv() {
if (document.getElementById('my-div-id')) {
return;
}
const myDiv = document.createElement('div');
myDiv.id = 'my-div-id';
... // Some async code that waits until the container is loaded to the DOM using setInterval
const container = document.querySelector('div.container');
container.appendChild(myDiv);
}
Using throttle to limit the url triggers sent from the background script to the content script:
const throttledNav = throttle(onNavigation, 2000, { leading: true, trailing: true });
chrome.webNavigation.onHistoryStateUpdated.addListener(throttledNav, { url: ... });
It looks like it helped with most cases but I still get a double (or even a triple) duplicate DOM injection of my div from time to time, which makes my extension look very buggy.
What can I do to fix this?
All,
Am using cordova inappbrowser. I load the a page uisng window.open. Then hook to loadstop and execute a javascript . The result of execution is another web page. Now I want to manipulate the document elements in that page. So i hook to another laodstop event, but that is not getting my second page. Some docs show , executescript returns an array, but not sure where the html dom is contained.
<script>
var inAppBrowserRef;
function OpenSRMS() {
inAppBrowserRef = window.open('myink','_blank','location=no');
inAppBrowserRef.addEventListener('loadstop', loginSRMS);
}
function loginSRMS() {
if (inAppBrowserRef != undefined) {
inAppBrowserRef.executeScript({ code: "document.getElementById('userName').value = 'Training';" +
"document.getElementById('password').value = 'Training';" +
"document.getElementById(\"frm\").submit();"});
}
inAppBrowserRef.addEventListener('loadstop', INQCS);
}
function INQCS(){
if (inAppBrowserRef != undefined) {
inAppBrowserRef.executeScript({ code: "alert('hello');"});
}
}
</script>
This doesn't work in Safari:
<html>
<body>
<applet id="MyApplet" code="MyAppletClass" archive="MyApplet.jar">
<script type="text/javascript">
alert(document.getElementById('MyApplet').myMethod);
</script>
</body>
</html>
myMethod is a public method declared in MyAppletClass.
When I first load the page in Safari, it shows the alert before the applet has finished loading (so the message box displays undefined) . If I refresh the page, the applet has already been loaded and the alert displays function myMethod() { [native code] }, as you'd expect.
Of course, this means that the applet methods are not available until it has loaded, but Safari isn't blocking the JavaScript from running. The same problem happens with <body onLoad>.
What I need is something like <body onAppletLoad="doSomething()">. How do I work around this issue?
PS: I'm not sure if it's relevant, but the JAR is signed.
I use a timer that resets and keeps checking a number of times before it gives up.
<script language="text/javascript" defer>
function performAppletCode(count) {
var applet = document.getElementById('MyApplet');
if (!applet.myMethod && count > 0) {
setTimeout( function() { performAppletCode( --count ); }, 2000 );
}
else if (applet.myMethod) {
// use the applet for something
}
else {
alert( 'applet failed to load' );
}
}
performAppletCode( 10 );
</script>
Note that this assumes that the applet will run in Safari. I've had some instances where an applet required Java 6 that simply hangs Safari even with code similar to the above. I chose to do browser detection on the server and redirect the user to an error page when the browser doesn't support the applet.
Here is a generic function I wrote to do just this:
/* Attempt to load the applet up to "X" times with a delay. If it succeeds, then execute the callback function. */
function WaitForAppletLoad(applet_id, attempts, delay, onSuccessCallback, onFailCallback) {
//Test
var to = typeof (document.getElementById(applet_id));
if (to == "function") {
onSuccessCallback(); //Go do it.
return true;
} else {
if (attempts == 0) {
onFailCallback();
return false;
} else {
//Put it back in the hopper.
setTimeout(function () {
WaitForAppletLoad(applet_id, --attempts, delay, onSuccessCallback, onFailCallback);
}, delay);
}
}
}
Call it like this:
WaitForAppletLoad("fileapplet", 10, 2000, function () {
document.getElementById("fileapplet").getDirectoriesObject("c:/");
}, function () {
alert("Sorry, unable to load the local file browser.");
});
I had a similar problem some time ago and adding MAYSCRIPT to the applet tag solved my problem.
Take a peek at this page:
http://www.htmlcodetutorial.com/applets/_APPLET_MAYSCRIPT.html
Hope it helps!