Add element to website/tab from Safari extension - javascript

I am trying to create an extension for Safari that will add an element to the page/tab that I am currently on. In the example below I'd like to add the Send button to the page.
The issue for me is that the new element (button) will be added to the extensions html.
Html:
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function sendMessage() {
document.getElementById("textField").innerHTML="Sending message...";
safari.application.activeBrowserWindow.activeTab.page.dispatchMessage("hey", "there");
}
function respondToMessage(messageEvent) {
if(messageEvent.name === "gotIt")
document.getElementById("textField").innerHTML=messageEvent.message;
}
safari.self.browserWindow.addEventListener("message",respondToMessage,false);
</script>
</head>
<body> Message Sender Bar
<input type="button" value="Send" onclick="sendMessage()" >
<span id="textField">...waiting... </span>
</body>
</html>
Js:
var theBody = document.body;
var element = document.createElement("p");
element.id = "status";
element.style.cssText = "float:right; color:red";
element.textContent = "Waiting...";
theBody.insertBefore(element, theBody.firstChild);
function replyToMessage(aMessageEvent) {
if (aMessageEvent.name === "hey") {
document.getElementById("status").textContent="Message received.";
safari.self.tab.dispatchMessage("gotIt","Message acknowledged.");
}
}
safari.self.addEventListener("message", replyToMessage, false);

Related

Chrome not reloading object tag after linking to nonexistent file

I have the following index.html. The objective of the javascript below is to reload the #obj element's data tag, so that it can display multiple images. However, it is possible that one of the images I link the buttons to doesn't exist (in this case, #2).
function updateObject(evt) {
var id = evt.currentTarget.id;
var object = document.getElementById("obj");
if (id == "1") {
object.setAttribute("data","https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg")
}
else {
object.setAttribute("data", "file/that/doesnt/exist")
}
}
for (var i = 0; i < document.getElementsByTagName("button").length; i++) {
document.getElementsByTagName("button")[i].addEventListener("click", updateObject, false);
}
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
</head>
<body>
<button id="1">button1</button>
<button id="2">button2</button>
<object id="obj" style='width: 100px'></object>
</body>
</html>
What I expect to happen in the following script is this:
The user presses button1, sees apple
User presses button2, sees nothing
User presses button1, sees apple
However, the third step in that doesn't happen - when I try to reload the object's data after linking to a nonexistent file, it stays blank.
As far as I've been able to gather, this happens in Chrome, and for me works in Safari. I must use the object tag, or some other method that allows for interactive SVG.
One solution you could possibily do is to remove and add the node itself to force a hard reset
var clone = object.cloneNode();
var parent = object.parentNode;
parent.removeChild(object);
parent.appendChild(clone);
function updateObject(evt) {
var id = evt.currentTarget.id;
var object = document.getElementById("obj");
if (id == "1") {
object.setAttribute("data", "https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg")
var clone = object.cloneNode();
var parent = object.parentNode;
parent.removeChild(object);
parent.appendChild(clone);
} else {
object.setAttribute("data", "file/that/doesnt/exist")
}
}
for (var i = 0; i < document.getElementsByTagName("button").length; i++) {
document.getElementsByTagName("button")[i].addEventListener("click", updateObject, false);
}
<button id="1">button1</button>
<button id="2">button2</button>
<object id="obj" style='width: 100px'></object>
Try changing the tag to an <img> and setting the "src" attribute.
function updateObject(evt) {
var id = evt.currentTarget.id;
var object = document.getElementById("obj");
if (id == "1") {
object.setAttribute("src","https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg")
}
else {
object.setAttribute("src", "file/that/doesnt/exist")
}
}
for (var i = 0; i < document.getElementsByTagName("button").length; i++) {
document.getElementsByTagName("button")[i].addEventListener("click", updateObject, false);
}
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
</head>
<body>
<button id="1">button1</button>
<button id="2">button2</button>
<img id="obj" style='width: 100px'></img>
</body>
</html>
I provide a sample which helps you to solve your problem by making a fake request to that URL.
Chrome does it to inform. Even if you handle onerror correctly with correct error handling with try-catch and every trick with a void or ( ) that is told to prevent error - you can not fix it. It is out of Javascript control.
function updateObject(evt) {
var id = evt.currentTarget.id;
var object = document.getElementById("obj");
if (id == "1") {
object.setAttribute("data","https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg");
}
else {
var request;
if(window.XMLHttpRequest)
request = new XMLHttpRequest();
else
request = new ActiveXObject("Microsoft.XMLHTTP");
request.open('GET', 'file/that/doesnt/exist', false);
request.send();
// the object request will be actually modified
if (request.status === 404) {
alert("The file you are trying to reach is not available.");
}
else
{
object.setAttribute("data", "file/that/doesnt/exist");
}
}
}
for (var i = 0; i < document.getElementsByTagName("button").length; i++) {
document.getElementsByTagName("button")[i].addEventListener("click", updateObject, false);
}
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
</head>
<body>
<button id="1">button1</button>
<button id="2">button2</button>
<object id="obj" style='width: 100px'></object>
</body>
</html>
But notice that it will only work on the same origin. For another host, you will have to use a server-side language to do that, which you will have to figure it out by yourself.

Detecting a click after a new element is loaded after the DOM

I am trying to create a simple Chrome Plugin - however I have come to an issue.
I am trying to detect a click on a div using a simple getElementById - however as the api call happens after the DOM is loaded the JS cannot 'find' any div's and gives an error and doesn't do anything after I click on the element.
How do I detect the click, after the data from the API has loaded? I have included some of my code below:
Thanks
document.addEventListener('DOMContentLoaded', function () {
var checkPageButton = document.getElementById('checkPage');
checkPageButton.addEventListener('click', function () {
inputBox = document.getElementById("postcodeInput").value
console.log(inputBox)
let xml = new XMLHttpRequest();
xml.open('get', "https://api.getaddress.io/find/" + inputBox + "/?api-key=SECRET&expand=true", false);
xml.send(null);
var data = xml
var arr = xml.responseText
var data = JSON.parse(arr)
var postcode = data.postcode
var addresses = data.addresses
console.log(addresses)
document.getElementById("postcode").innerHTML = postcode;
var text = "";
var i;
for (i = 0; i < addresses.length; i++) {
text += "<div id='addressClick' name=" + i + ">" + addresses[i].line_1 + "</div>" + "<br>";
}
document.getElementById("data").innerHTML = text;
clickFunc()
}, false);
}, false);
function clickFunc() {
var rowBox = document.getElementById("addressClick");
rowBox.addEventListener('click', function () {
console.log('asd');
}, true);
}
HTML
<!doctype html>
<html>
<head>
<title>Address Search</title>
<script src="popup.js"></script>
</head>
<body>
<h3>Address Search</h3>
<input type="text" id='postcodeInput' name="postcodeInput" value="KW1 4YT">
<button id="checkPage">Search</button>
<div class='results'>
<h3>Results - <span id='postcode'></span></h3>
<p id='data'></p>
</div>
</body>
</html>
<style>
body {
width: 200px
}
#addressClick:hover {
color: blue;
cursor: pointer
}
</style>
You can attach an EventListener to all the body and, at every click, detect if the clicked element is the desired one:
document.body.addEventListener('click', event => window.alert(event.target.innerText));
This can sound like an aggressive solution, but it's way less invasive than a MutationObserver

How to program a button to change stylesheets with javascript

Please note that I am not using classes. I haven't found an answer for this SPECIFIC question.
Using javascript, how can I program a button to change the stylesheet each time the button is clicked?
I've tried different if, else if and else, but when I try them, it breaks the code (ie, it will change the color to blue if red, but not back again).
It works with 2 buttons, but getting it to change each time a single button is clicked seems to be eluding me. I got feed up and programmed a second button to change it back.
This works for 2 buttons:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>"Your Title Here"</title>
<link id="um" rel="stylesheet" href="stylesheet1.css">
<style>
</style>
</head>
<body>
<p>booga</p>
<button id="x" onclick="myFunction()">blue</button>
<button id="x1" onclick="myFunction1()">red</button>
<script>
function myFunction() {
if (document.getElementById("um").href = "stylesheet1.css"){
document.getElementById("um").href = "stylesheet2.css"}
}
function myFunction1() {
if (document.getElementById("um").href = "stylesheet2.css"){
document.getElementById("um").href = "stylesheet1.css"}
}
</script>
</body>
I would like to be able to get rid of the second button and second function and have it all with one button.
EDIT...
I tried this, and it failed.
function myFunction() {
if (document.getElementById("um").href == "stylesheet1.css")
{document.getElementById("um").href = "stylesheet2.css"};
else {document.getElementById("um").href = "stylesheet1.css"}
}
Make sure you're using == instead of = for your comparisons!
if (document.getElementById("um").href == "stylesheet1.css")
etc
Try this:
<button id="x" onclick="myFunction()">Change</button>
<script>
function myFunction() {
var link = document.getElementById("um");
var segments = link.href.split('/');
var currentStyle = segments[segments.length - 1];
var style = (currentStyle == 'stylesheet1.css') ? 'stylesheet2'
: 'stylesheet1';
document.getElementById("um").href = style + ".css"
}
</script>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>"Your Title Here"</title>
<link id="um" rel="stylesheet" href="stylesheet1.css">
</head>
<body>
<p>booga</p>
<button onclick="myFunction('um','stylesheet1.css', 'stylesheet2.css')">swap</button>
<script>
function myFunction(id,a,b) {
var el = document.getElementById(id);
var hrefStr;
if(~el.href.indexOf(a)) {
hrefStr = el.href.replace(a, b);
el.href = hrefStr;
} else {
hrefStr = el.href.replace(b, a);
el.href = hrefStr;
}
}
</script>
</body>
</html>

Create and save new list item with Javascript and HTML 5

I have the following code.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function addChildElements()
{
var newdiv=document.createElement("li");
var newtext=document.createTextNode("test");
newdiv.appendChild(newtext);
document.getElementById("scribble").appendChild(newdiv);
}
</script>
<script>
function storeUserScribble(id) {
var scribble = document.getElementById('scribble').children;
localStorage.setItem('userScribble',scribble);
}
function getUserScribble() {
if ( localStorage.getItem('userScribble')) {
var scribble = localStorage.getItem('userScribble');
}
else {
var newdiv=document.createElement("li");
var newtext=document.createTextNode("test");
newdiv.appendChild(newtext);
document.getElementById("scribble").appendChild(newdiv);
}
document.getElementById('scribble').children = scribble;
}
function clearLocal() {
clear: localStorage.clear();
return false;
}
</script>
</head>
<body>
<ol id="scribble" contenteditable="false" onclick="storeUserScribble(this.id);">
</ol>
<input type="button" value="Add Child Element" onclick="addChildElements(); storeUserScribble(document.getElementById('scribble').id);"/>
</body>
</html>
Which is meant to save the list items as they are created, but i'm completely at a loss. This code doesn't work. I know it probably has something to do with:
var scribble = document.getElementById('scribble').children;
or
onclick="addChildElements(); storeUserScribble(document.getElementById('scribble').id);"
But that is about where my JS knowledge stops.
Any help is much appreciated.

IE losing iframe contents after back/forward key

This problem is only happening in IE (at least 8 and 9). After an element is dynamically added to the DOM, the contents of an embedded iframe are lost when the page is reentered with a BACK/FORWARD key. Just two small HTML files will reproduce the issue.
The first file is iframe.htm:
<!DOCTYPE html>
<html>
<head>
<title>IE iframe bug</title>
<script type="text/javascript">
function mytrace(msg) {
var t = document.createTextNode(msg);
var b = document.createElement('br');
var d = document.getElementById("trace_output")
d.appendChild(t);
d.appendChild(b); /// will work if commented
}
function submitListing() {
mytrace('submitListing()');
var doc = document.getElementById("output_iframe")
.contentWindow.document;
var d = new Date;
doc.location.replace('report.htm?invalidateCache=' + d.getTime());
//mytrace('submitListing(): out');
}
</script>
</head>
<body>
<div id="trace_output"><br /></div>
<input type="button" onclick="submitListing();" value="Run" /><br />
<iframe id="output_iframe" src=""></iframe>
</body>
</html>
The second file is report.htm:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
LINK
</body>
</html>
Steps to recreate the issue (BACK KEY)
Place above content in two files
Browse the iframe.htm file
Press the Run button to load report.htm in the iframe
Click on the LINK link to load a different page
Press the browser BACK button to returned to the "cached" (lmao) page
iframe contents are gone!!!! (only in IE-- safari, chrome, firefox retain the contents)
Also..(FORWARD KEY)
Browse to an arbitrary page (for history, http://www.google.com works)
Load iframe.htm into the same tab
Press the Run button to load report.htm in the iframe
Press the browser BACK button to return to the first page
Press the browser FORWARD button to return to iframe.htm
iframe contents are gone again!!
Now comment out the line:
d.appendChild(b)
That one change allows everything to work in IE. However, my solution needs to make those kinds of DOM manipulations (heavy jQuery/AJAX app) AND be able to restore the iframe across browser BACK/FORWARD actions.
It seems that I will have to remember the contents of the iframe so that I can restore it when the page is accessed with the BACK/FORWARD keys. I'm not thrilled with this because sometimes the iframe content will be quite large and it could chew up a bit of memory and time to make another copy of the embedded document for the restore. I would love to hear some other ideas about how I could approach this. Thanks in advance.
EDIT
The following replacement to iframe.htm will work around the problem with IE. I'm going to rewrite this using jQuery and add some more logic to restore the scroll positions. I had hoped for something more elegant, but this is doing the job.
<!DOCTYPE html>
<html>
<head>
<title>IE iframe bug</title>
<script type="text/javascript">
function myTrace(msg) {
var t = document.createTextNode(msg);
var b = document.createElement('br');
var d = document.getElementById("trace_output")
d.appendChild(t);
d.appendChild(b);
}
var make_backup ="false";
function submitListing() {
make_backup = "true";
myTrace('submitListing()');
var doc = document.getElementById("output_iframe").contentWindow.document;
var d = new Date;
doc.location.replace('report.htm?invalidateCache=' + d.getTime());
//myTrace('submitListing(): out');
}
function iframe_load() {
myTrace("iframe loaded, is_cached=" + document.getElementById("is_cached").value);
if (make_backup == "true") { // only when submitting
var htm, doc;
make_backup = "false"
doc = document.getElementById("output_iframe").contentWindow.document;
htm = doc.documentElement.innerHTML;
document.getElementById("iframe_backup").value = htmlEscape(htm);
}
}
function bodyLoaded() {
var is_cached = document.getElementById("is_cached");
if (is_cached.value == "false") { // initial page load
is_cached.value = "true";
}
else { // BACK or FORWARD, restore DOM where needed
var htm;
htm = htmlUnescape(document.getElementById("iframe_backup").value);
var doc;
doc = document.getElementById("output_iframe").contentWindow.document;
doc.open();
doc.writeln(htm);
doc.close();
}
}
function htmlEscape(str) {
return String(str).replace(/&/g, '&').replace(/"/g, '"')
.replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
}
function htmlUnescape(str) {
return String(str).replace(/&/g,'&').replace(/"/g,'"')
.replace(/'/g,"'").replace(/</g,'<').replace(/>/g,'>');
}
</script>
</head>
<body onload="bodyLoaded();">
<div id="trace_output" style="height: 300px; border-width:1; background-color: Silver"><br></div>
<input id="is_cached" type="hidden" value="false">
<input id="iframe_backup" type="hidden">
<input type="button" onclick="submitListing();" value="Run"><br>
<iframe id="output_iframe" src="" onload="iframe_load();"></iframe>
</body>
</html>
EDIT 2
Rewritten with jQuery:
<!DOCTYPE html>
<html>
<head>
<title>IE iframe workaround2</title>
<script type="text/javascript" src="Scripts/jquery-1.7.1.js"></script>
<script type="text/javascript">
var make_backup = "false";
$(document).ready(function () {
myTrace('document ready()');
var is_cached = $("#is_cached");
if (is_cached.val() == "false") { // initial page load
is_cached.val("true");
}
else { // BACK or FORWARD, restore DOM where needed
if ($.browser.msie) { // IE loses iframe content; restore
var htm = htmlUnescape($("#iframe_backup").val());
var doc = $("#output_iframe")[0].contentWindow.document;
doc.open();
doc.writeln(htm);
doc.close();
myTrace('iframe contents restored');
}
}
$('#output_iframe').load(function () {
myTrace("iframe_loaded");
if (make_backup == "true") { // only when submitting
make_backup = "false"
if ($.browser.msie) {
var doc = $("#output_iframe")[0].contentWindow.document;
var htm = doc.documentElement.innerHTML;
$("#iframe_backup").val(htmlEscape(htm));
myTrace('iframe contents backed up');
}
}
});
$('#submit_listing').click(function () {
make_backup = "true";
myTrace('submitListing()');
var doc = $("#output_iframe")[0].contentWindow.document;
var d = new Date;
doc.location.replace('report.htm?invalidateCache='+d.getTime());
});
});
function myTrace(msg) {
$('#trace_output').append(msg + '<br>');
}
function htmlEscape(str) {
return String(str).replace(/&/g, '&').replace(/"/g, '"')
.replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
}
function htmlUnescape(str) {
return String(str).replace(/&/g,'&').replace(/"/g,'"')
.replace(/'/g,"'").replace(/</g,'<').replace(/>/g,'>');
}
</script>
</head>
<body>
<div id="trace_output"
style="height: 300px; border-width:1; background-color: Silver">
<br></div>
<div style="display: block;">
<input id="is_cached" type="text" value="false">
<input id="iframe_backup" type="text" type="hidden"></div>
<input id="submit_listing" type="button" value="Run"><br>
<iframe id="output_iframe" src=""></iframe>
</body>
</html>

Categories