Updating page HTML causes endless loop - javascript

I'm writing a Firefox extension. The extension replaces certain words on the page with other words. Here's the basic code I'm using:
function startup() {
gBrowser.addEventListener("load", pageLoad, true);
}
function pageLoad(event) {
if (event.originalTarget instanceof HTMLDocument) {
var ht = content.document.body.innerHTML;
ht = ht.replace(/\bthe\b/g,"el");
content.document.body.innerHTML = ht;
}
}
The problem is that this code is causing an endless loop. When I set the innerHTML property of the body, it sends another load event, which causes the endless loop.
How can I modify the page when it loads without causing the page load event to fire again?

You could use the following code to check if it has already been run before.
var loaded = false;
function pageLoad(event) {
if (!loaded) {
if (event.originalTarget instanceof HTMLDocument) {
var ht = content.document.body.innerHTML;
ht = ht.replace(/\bthe\b/g,"el");
content.document.body.innerHTML = ht;
}
loaded = true;
}
}
Alternatively, if you wanted to keep the loaded variable out of global scope, you could use a closure:
var pageLoad = (function () {
var loaded = false;
return function(event) {
if (!loaded) {
if (event.originalTarget instanceof HTMLDocument) {
var ht = content.document.body.innerHTML;
ht = ht.replace(/\bthe\b/g,"el");
content.document.body.innerHTML = ht;
}
loaded = true;
}
}
}();
The outer function gets executed immediately and returns the inner function which will have visibility of the loaded variable due to closure.

Would having a div or span tag immediately inside of your body tag be an option? Then you could just update the innerHTML of that element, and not the entire body...

Related

When using the Microsoft Translator, Is there a way to remove the widget, but retain translation?

I'm using the Microsoft Translation Widget, which I'd like to use to automatically translate a webpage without user interaction.
The problem is, I can't get rid of the widget that keeps popping up or hide it on document.ready because the CSS and JS get loaded from Microsoft's own script in the widget!
Does anyone know a way around this? I've looked everywhere and cannot find a solutuion for this.
Whoa, after some time playing around with that, I've finally achieved what you want.
It's kindda ugly, because of some needed workarounds, but it works, take a look at the fiddle.
The steps were:
Firstly, we must override the default addEventListener behavior:
var addEvent = EventTarget.prototype.addEventListener;
var events = [];
EventTarget.prototype.addEventListener = function(type, listener) {
addEvent.apply(this, [].slice.call(arguments));
events.push({
element: this,
type: type,
listener: listener
});
}
Then, we create a helper function removeEvents. It removes all the event listeners of an element.
var removeEvents = function(el, type) {
var elEvents = events.filter(function(ev) {
return ev.element === el && (type ? ev.type === type : true);
});
for (var i = 0; i < elEvents.length; i++) {
el.removeEventListener(elEvents[i].type, elEvents[i].listener);
}
}
When creating the script tag, in the way Microsoft says:
var s = d.createElement('script');
s.type = 'text/javascript';
s.charset = 'UTF-8';
s.src = ((location && location.href && location.href.indexOf('https') == 0) ? 'https://ssl.microsofttranslator.com' : 'http://www.microsofttranslator.com') + '/ajax/v3/WidgetV3.ashx?siteData=ueOIGRSKkd965FeEGM5JtQ**&ctf=True&ui=true&settings=Manual&from=';
var p = d.getElementsByTagName('head')[0] || d.dElement;
p.insertBefore(s, p.firstChild);
We must add a load event listener to that script, and the code below is fully commented:
s.addEventListener('load', function() {
// when someone changes the translation, the plugin calls the method TranslateArray
// then, we save the original method in a variable, and we override it
var translate = Microsoft.Translator.TranslateArray;
Microsoft.Translator.TranslateArray = function() {
// we call the original method
translate.apply(this, [].slice.call(arguments));
// since the translation is not immediately available
// and we don't have control when it will be
// I've created a helper function to wait for it
waitForTranslation(function() {
// as soon as it is available
// we get all the elements with an attribute lang
[].forEach.call(d.querySelectorAll('[lang]'), function(item, i) {
// and we remove all the mouseover event listeners of them
removeEvents(item, 'mouseover');
});
});
}
// this is the helper function which waits for the translation
function waitForTranslation(cb) {
// since we don't have control over the translation callback
// the workaround was to see if the Translating label is visible
// we keep calling the function, until it's hidden again
// and then we call our callback
var visible = d.getElementById('FloaterProgressBar').style.visibility;
if (visible === 'visible') {
setTimeout(function() {
waitForTranslation(cb);
}, 0);
return;
}
cb();
}
});
Update 1
After re-reading your question, it seems you want to hide all the widgets at all.
So, you must add the following code as soon as the translation is got:
waitForTranslation(function() {
document.getElementById('MicrosoftTranslatorWidget').style.display = 'none';
document.getElementById('WidgetLauncher').style.display = 'none';
document.getElementById('LauncherTranslatePhrase').style.display = 'none';
document.getElementById('TranslateSpan').style.display = 'none';
document.getElementById('LauncherLogo').style.display = 'none';
document.getElementById('WidgetFloaterPanels').style.display = 'none';
// rest of the code
});
I've created another fiddle for you, showing that new behavior.
Update 2
You can prevent the widget showing at all by adding the following CSS code:
#MicrosoftTranslatorWidget, #WidgetLauncher, #LauncherTranslatePhrase, #TranslateSpan, #LauncherLogo, #WidgetFloaterPanels {
opacity: 0!important;
}
And you can even prevent the before-translated text being showed, by hiding the document.body by default, and then showing it when the page is fully translated:
(function(w, d) {
document.body.style.display = 'none';
/* (...) */
s.addEventListener('load', function() {
var translate = Microsoft.Translator.TranslateArray;
Microsoft.Translator.TranslateArray = function() {
translate.apply(this, [].slice.call(arguments));
waitForTranslation(function() {
/* (...) */
document.body.style.display = 'block';
});
}
});
});
Take a look at the final fiddle I've created.
For me, this was the solution:
on your < style > section add this class
.LTRStyle { display: none !important }
Also, if you are invoking the translation widget this way:
Microsoft.Translator.Widget.Translate('en', lang, null, null, TranslationDone, null, 3000);
then add this to your callback (in this example is TranslationDone) function:
function TranslationDone() {
Microsoft.Translator.Widget.domTranslator.showHighlight = false;
Microsoft.Translator.Widget.domTranslator.showTooltips = false;
document.getElementById('WidgetFloaterPanels').style.display = 'none';
};

addEventListener won't work because of null

All I'm trying to do is have the user click the button, and when that event occurs I want an image to display within a div. I inspected the element, and it says that tableButton is undefined, but I defined it right before the addEventListener. What am I doing wrong? Sorry, I'm new to javascript.
function openTable() {
var code = "<img src='PeriodicTableOfElements.png'>";
var periodic = document.getElementById("Periodic");
periodic.innerHTML = code;
}
var tableButton = document.getElementById("openTable");
tableButton.addEventListener("click", openTable, false);
Have you made sure to wrap it in window.onload, elements can't exist if the window hasn't loaded.
window.onload = function(){
function openTable() {
var code = "<img src='PeriodicTableOfElements.png'>";
var periodic = document.getElementById("Periodic");
periodic.innerHTML = code;
}
var tableButton = document.getElementById("openTable");
tableButton.addEventListener("click", openTable, false);
}

Cannot detect if window is loaded

I'm currently working on counting the number of opened tabs on my application. but my problem is it seems that my script won't detect events onload. Here is my code.
I'm using HTML5 web storage and native js. I'm not using jQuery to understand more on native js.
(function(w) {
function Tabz(win, key) {
this.name = '';
this.storageKey = key;
if(win.name != '')
this.name = win.name;
else {
var windowArr = JSON.parse(localStorage.getItem(key)) || [];
this.name = "tabz_"+ windowArr.length;
win.name = this.name;
windowArr.push(this.name);
localStorage.setItem(this.storageKey, JSON.stringify(windowArr) );
}
}
Tabz.prototype.getStorage = function() {
return localStorage.getItem(this.storageKey);
}
Tabz.prototype.removeWindow = function() {
//remove window function here
}
var newWindow = new Tabz(w, 'counter');
window.load = function() {
var count = JSON.parse(newWindow.getStorage()).length;
alert(count!); // this wont execute so that I can check the count.
}
})(window);
Your issue is on this line:
window.load = function() {
This will add a load property to the window, not add an event listener. I think you are looking for onload.
window.onload = function() {
Incidentally, using event properties is considered bad-practice. Using addEventListener would be better.
window.addEventListener("load", function(){
//Do stuff...
});

processing.js change script source

I need to find a way where I can dynamically change the source of a processing script inside an HTML document.
This is the embedded script which works fine:
<script type='application/processing' src='sketch.txt' id="applet">
</script>
Now I try to change the source:
$('#applet').attr('src', 'sketch2.txt');
alert($('#applet').attr('src'));
The alert shows that the source was changed to 'sketch2.txt' but the applet still remains the same. I think I need to refresh the script in some way.
Thank you in advance for any help!
I believe you have to manually attach each proc to the canvas, instead of just changing the the source file. This worked here:
function first_call(processing) {
processing.size(300,300);
processing.background(100);
var called = false;
processing.draw = function() {
if (!called) { processing.println("called #1"); called = true; }
}
}
function second_call(processing) {
processing.size(400,400);
processing.background(200);
var called = false;
processing.draw = function() {
if (!called) { processing.println("called #2"); called = true; }
}
}
var canvas = document.getElementById('canvas1');
var processingInstance = new Processing(canvas, first_call);
var processingInstance = new Processing(canvas, second_call);

roll-over script not working

I am trying to make a mouse roll-over. i.e when user places the pointer on the image mouse over event is triggered and the next image from the array comes over.But nothing is happening when i am running this script :
window.onload = startRollOver;
var pictures = new Array("1.jpg","2.jpg","3.jpg","4.jpg","5.jpg","6.jpg","7.jpg","8.jpg");
var i = 0;
function startRollOver() {
document.getElementById("picture").src.onmouseover = createRollOver();
}
function createRollOver() {
if(i<=7)
return pictures[i++];
if(i>7) {
i=0;
return pictures[i];
}
}
Where am i going wrong ?
.src.onmouseover does not make much sense. You should assign a function to onmouseover so that the function gets called when the mouse moves over the element:
document.getElementById("picture").onmouseover = function() { // is executed when mouse is over element
this.src = createRollOver(); // each time it is called, change the src
};
Also, you can use a more convenient way of declaring arrays:
["1.jpg", "2.jpg", ...]
You're setting "onmouseover" on the string "src" instead of on the DOM element. You need to set the event on the DOM element.
window.onload=startRollOver;
function startRollOver() {
document.getElementById("picture").onmouseover = function (e) {
e.target.src = createRollOver();
};
}
function createRollOver() {
// ...
}

Categories