Will JavaScripts embedded in an HTML file loaded by WKWebView be accessible? - javascript

I have a basic foo.html in my iOS 10 application. The markup is straight forward:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>Hello, World!</p>
<div id="container"></div>
</body>
<script type="text/javascript" src="bar.js"></script>
<script type="text/javascript">
// Bar is defined in bar.js
var bar = new Bar();
</script>
</html>
I load it with the following:
let htmlFile = Bundle.main.path(forResource: "foo", ofType: "html")
let html = try? String(contentsOfFile: htmlFile!, encoding: String.Encoding.utf8)
webView.loadHTMLString(html!, baseURL: nil)
In my iOS app, I'm trying to access the bar variable. After the DOM is loaded as confirmed by by WKNavigationDelegate's method: func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
I have code like this:
var htmlContent : String {
var s = "console.log(bar)"
return s
}
webView.evaluateJavaScript(htmlContent, completionHandler: { result, error in
if let error = error {
print("error: \(error)")
}
if let result = result {
print("result: \(result)")
}
})
I end up getting an error:
error: Error Domain=WKErrorDomain Code=4 "A JavaScript exception occurred" UserInfo={WKJavaScriptExceptionLineNumber=1, WKJavaScriptExceptionMessage=ReferenceError: Can't find variable: Bar, WKJavaScriptExceptionSourceURL=about:blank, NSLocalizedDescription=A JavaScript exception occurred, WKJavaScriptExceptionColumnNumber=47}
Is what I'm trying to do possible? Do I have to approach the problem a different way? Does evaluateJavaScript have access to the scope of my DOM after it's loaded because as of right now, it seems it does not.

I figured it out - will put the answer here hoping it helps someone else:
The problem was here:
webView.loadHTMLString(html!, baseURL: nil)
I had to ensure baseURL is not nil, the following fixes it:
webView.loadHTMLString(html!, baseURL: Bundle.main.bundleURL)

Related

javascript fetch command does not display the content on the html page

I have a simple text file in the same directory as HTML file , I used the fetch command in javascript to display the text file content in the page div section when the loading of the page finish
however, my code doesn't work and nothing has been displayed, my question is does the fetch command suitable for such a task or should I use filereader ?
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Untitled Page</title>
<meta name="generator" >
<style type="text/css">
</style>
<script type="text/javascript" defer>
const myfile=location.href.slice(0,location.href.lastIndexOf("/"))+"/a.txt"
console.log(myfile);
async function getTextFile1() {
try {
const response = await fetch(myfile, { mode: 'no-cors' });
const fileText = await response.text();
//console.log(window.location);
//console.log(window.location.href);
const tagElement = document.getElementById("about_layer");
tagElement.innerText = fileText;
} catch (error) {
console.log(error);
}
}
window.onload = getTextFile1;
</script>
</head>
<body>
<div >should be placed here </div>
<div id="about_layer">
</div>
</body>
</html>
There is nothing wrong with your code , just try running your html file using development server .
You can use Live Server visual studio code extension for that.

Access to javascript module Class/object from index.html

I defined Class in my javascript file...I imported that file into html page:
<script type="module" src="./js/controller.js"></script>
How can I now acces to classes inside of that js file?
I want to have something like this (in my html file):
<script>
let app = null;
document.addEventListener('DOMContentLoaded', function () {
//Init app on DOM load
app = new MyApp();
});
</script>
But it doesn't work (I get Uncaught ReferenceError: MyApp is not defined)...If I include this DOMContentLoaded listener into end of my controller.js file, It works. But I lost reference to app variable this way (which I don't want)... Is there way to have reference to something defined in modules?
Most important reason why I want to have that reference is ability to access to my app object from google chrome console...
Thanks!
You can access your class in js file from html in the following way-
My Home.html file:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Page Title</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<script type="module">
import { Car } from "./main.js";
let obj= null;
alert("Working! ");
obj = new Car("Mini", 2001);
obj.PrintDetails();
document.addEventListener('DOMContentLoaded', function () {
let obj2 = new Car("Merc", 2010);
obj2.PrintDetails();
});
</script>
</head>
<body>
<h1> Lets try something <br></h1>
</body>
</html>
My main.js file:
export class Car {
constructor(name, year) {
this.name = name;
this.year = year;
}
PrintDetails() {
console.log(" Name = "+ this.name);
console.log(" year = "+ this.year);
}
}

Routing(?) in Vanilla JS

I need my webite to display info in a certain language, based on a query in my webite's URL (e.g. www.website.com/index.php?country=FR). How can I do that with vanilla JS and not React/Angular?
My approach:
1) JS recognizes a query in the URL (in this case- 'country=FR') and then appends a js file, which has neccessary french words in it defined by variables.
2) JS in my script tag that's in the HTML file, appends the main page markup text with template literals in it.
3)
I don't know, whether the browser fails to either fetch the language file itself or its variables. At the moment it does not render anything.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./js/main.js"></script>
</head>
<body>
<script>
const template= `
<h1>Good Morning: ${goodmorning} </h1>
<h2>Good Evening: ${goodevening} </h2>
<h3>My name is: ${mynameis}</h3>`
function markupAppend() {
$('body').html(template);
console.log('Markup loaded')
}
markupAppend()
</script>
</body>
</html>
=========================
Main.js
var domain = window.location.href;
var FRString = domain.includes("country=FR");
var ESString = domain.includes("country=ES");
if (FRString) {
$('head').append(`<script src="./Language_files/FRENCHwords.js" />`)
}
if (ESString) {
$('head').append(`<script src="./Language_files/SPANISHwords.js" />`)
}
=========================
FRENCHwords.js
const goodmorning = 'Bonjour';
const goodevening = 'Bonsoir';
const mynameis = 'Mon nom est';
=========================
SPANISHwords.js
const goodmorning = 'Buenos dias';
const goodevening = 'Buenas tardes';
const mynameis = 'Mi nombre es';
No errors displayed, the page is just not rendering...
In Your main.js file, you are using domain.includes, it only returns the domain name but not the entire URL. You can use window.location.href.includes for this.
Instead of: domain.includes("country=FR");
Try: window.location.href.includes("country=FR");

Typescript error - Property 'permission' not exists on type

I have this javascript code that shows the current status of notification permission:
main.js
var $status = document.getElementById('status');
if ('Notification' in window) {
$status.innerText = Notification.permission;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<p>Current permission status is
<b id="status">unavailable</b>
</p>
<script src="/scripts/main.js"></script>
</body>
</html>
If I write the same code in a typescript file I am getting this error:
main.ts
var $status = document.getElementById('status');
if ('Notification' in window) {
$status.innerText = Notification.permission;
}
ERROR - Notification.permission
MESSAGE - Property 'permission' not exists on type
'new(title: string, options?: NotificationOptions): Notification;
prototype: Notification;
requestPermission(callback?: NotificationPermissionCallback):
Promise<string>;'
How to ignore this error?
Try casting Notification to the any type to avoid transpiler errors.
if ('Notification' in window) {
$status.innerText = (Notification as any).permission;
}
The other option is to include the Notification type's definition.

Firefox not executing scripts loaded dynamically via another external script

I've been having trouble with Firefox not executing JavaScript files that were loaded dynamically via an external script.
Let me explain.
I have the following HTML file:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Restive.JS</title>
<meta name="keywords" content="">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript" src="assets/js/load.js"></script>
</head>
<body>
<h1>Loading JavaScript</h1>
</body>
</html>
Inside my load.js file, I have the following code:
document.addEventListener("DOMContentLoaded", function() {
function loadScript(url) {
var script = document.createElement("script");
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
var list_arr = ['assets/js/test1.js', 'assets/js/test2.js'];
for (var i = 0; i < list_arr.length; i++) {
loadScript(list_arr[i]);
}
});
And inside test1.js and test2.js, I have console.log('test1.js is loaded!'); and console.log('test2.js is loaded!');.
The problem is that test1.js and test2.js are loaded (I can see both files in the <head> via inspection), but they are never executed (because no messages appear in the console log).
However, when I change the format of script reference in my original HTML by inlining the JavaScript i.e. changing from this:
<script type="text/javascript" src="assets/js/load.js"></script>
to this:
<script>
document.addEventListener("DOMContentLoaded", function() {
function loadScript(url) {
var script = document.createElement("script");
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
var list_arr = ['assets/js/test3.js', 'assets/js/test4.js'];
for (var i = 0; i < list_arr.length; i++) {
console.log('i = ' + i);
loadScript(list_arr[i]);
}
});
</script>
Then the scripts are loaded and executed.
I don't see this behaviour in Chrome or Safari, only Firefox. Also, inlining is not an option because this functionality is built-in to a library that users will have to reference via an external link.
Is this a problem that is fixable?
EDIT
I'm on a Mac OSX 10.10.5 using Firefox 46.0.1

Categories