Can anyone explain why this JavaScript causes memory leaks in IE7? - javascript

The code is rather long yet simple:
100 leaky JavaScript objects are created.
10 leaky elements are created from the JS objects.
1 element is removed and 1 is added 10000 times.
I assume that the detachEvent call is not functioning properly.
Also, if you change this.eventParams from an array to a simple variable, the leak goes away. Why?
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Memory Leak With Fix</title>
<style type="text/css">
.leakyEle
{
border: solid 1px red;
background-color: Gray;
}
</style>
<script type="text/javascript">
/******************************* MAIN ********************************/
var leakObjArray = new Array();
AddEvent(window, 'load', Startup, false);
function Startup() {
for(var i=0; i<100; i++) {
leakObjArray.push(new LeakyObj(i));
}
for(var j=0; j<10; j++) {
leakObjArray[j].CreateLeakyEle();
}
var container = document.getElementById('Container');
AddEvent(container, 'click', Run, false);
alert('Close this dialog and click the document to continue.');
}
function Run() {
var k = 0;
var l = 10;
for(var m = 0; m<10000; m++) {
leakObjArray[k].DestroyLeakyEle();
leakObjArray[l].CreateLeakyEle();
if(k<leakObjArray.length - 1) {
k++;
} else {
k = 0;
}
if(l<leakObjArray.length - 1) {
l++;
} else {
l = 0;
}
}
for(var i=0; i<leakObjArray.length; i++) {
leakObjArray[i].DestroyLeakyEle();
}
alert('Test Complete.');
}
/******************************* END MAIN ********************************/
/******************************* LEAKY OBJECT ********************************/
function LeakyObj(id) {
this.id = id;
this.leakyEle = null;
this.containerEle = document.getElementById('Container');
this.clicked = false;
this.eventParams = new Array();
}
LeakyObj.prototype.CreateLeakyEle = function() {
var leakyEle = document.createElement('div');
leakyEle.id = 'leakyEle' + this.id;
leakyEle.className = 'leakyEle';
leakyEle.innerHTML = this.id + ' --- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' +
'<br/>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' +
'<br/>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' +
'<br/>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' +
'<br/>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
this.leakyEle = leakyEle;
var _self = this;
this.eventParams.push(AddEventWithReturnParams(this.leakyEle, 'click', function() { _self.EventHandler(); }, false));
this.containerEle.appendChild(leakyEle);
}
LeakyObj.prototype.DestroyLeakyEle = function() {
if(this.leakyEle != null) {
this.containerEle.removeChild(this.leakyEle);
for(var i=0; i<this.eventParams.length; i++) {
RemoveEventOverload(this.eventParams[i]);
}
this.leakyEle = null;
}
}
LeakyObj.prototype.EventHandler = function() {
this.leakyEle.style.display = 'none';
this.clicked = true;
}
/******************************* END LEAKY OBJECT ********************************/
/******************************* GENERAL FUNCS ********************************/
function AddEvent(elm, evType, fn, useCapture){
var success = false;
if(elm.addEventListener) {
if(evType == 'mousewheel') evType = 'DOMMouseScroll';
elm.addEventListener(evType, fn, useCapture);
success = true;
} else if(elm.attachEvent) {
if(evType == 'mousewheel') {
window.onmousewheel = document.onmousewheel = fn;
success = true;
} else {
var r = elm.attachEvent('on' + evType, fn);
success = r;
}
} else {
success = false;
}
elm = null;
return success;
}
function AddEventWithReturnParams(elm, evType, fn, useCapture) {
var eventParams = new EventParams(elm, evType, fn, useCapture);
AddEvent(elm, evType, fn, useCapture);
return eventParams;
}
function RemoveEvent(elm, evType, fn, useCapture) {
if(elm) {
if(elm.removeEventListener) {
elm.removeEventListener(evType, fn, useCapture);
return true;
} else if(elm.detachEvent) {
var r = elm.detachEvent('on' + evType, fn);
return r;
} else {
debugger;
}
}
}
function RemoveEventOverload(eventParams) {
if(eventParams) {
return RemoveEvent(eventParams.element, eventParams.eventType, eventParams.handler, eventParams.capture);
}
}
function EventParams(elm, evType, fn, useCapture) {
return {
element: elm,
eventType: evType,
handler: fn,
capture: useCapture
}
}
/******************************* END GENERAL FUNCS ********************************/
</script>
</head>
<body>
<div id="Container"></div>
</body>
</html>

If you take a look at your code, you should notice that in each eventParams object stored in your eventParams array, you have references to the objects, but you never empty out your array. Try clearing out your array...

looks like you're pushing stuff onto the eventParams array inside CreateLeakyEle, but never removing it? Is that right?

Related

How to check if a function has been called before executing another function everytime

I have a onMouseDownEssence() and onMouseUpEssence() function for an HTML element, how to check if onMouseDownEssence() is called every time before calling onMouseUpEssence() to ensure I get the correct mouse down position?
Here is mousedown function:
var mouseDownIndex = -1;
function onMouseDownEssence(downIndex, e, className) {
dragTarget = e.target;
holdStarter = new Date().valueOf();
mouseDownIndex = downIndex;
}
Here is mouseup function:
function onMouseUpEssence(upIndex, e, className) {
var el = e.target;
var holdActive = (new Date().valueOf() - holdStarter) > holdDelay;
if (holdActive) {
var thisUpTargetIndex = el.getAttribute("name");
if (lastUpTargetIndex != null && thisUpTargetIndex != lastUpTargetIndex) {
// console.log("double drag done");
el.removeAttribute(dbl);
lastUpTargetIndex = null;
var selectedText = clickDragAutoExpand(mouseDownIndex, upIndex,
className);
} else {
// console.log("drag done");
var selectedText = clickDragAutoExpand(mouseDownIndex, upIndex,
className);
}
holdActive = false;
} else if (el.getAttribute(dbl) == null) {
el.setAttribute(dbl, 1);
setTimeout(
function() {
if (el.getAttribute(dbl) == 1 && !dragTarget) {
if (e.button === 0) {
// console.log("single clicked ");
el.removeAttribute(dbl);
var selectedText = clickAutoExpand(upIndex,
className);
}
} else {
if (el.getAttribute(dbl) != null)
lastUpTargetIndex = el.getAttribute("name");
}
}, dblDelay);
} else {
// console.log("double clicked");
el.removeAttribute(dbl);
var selectedText = clickAutoExpand(upIndex, className);
}
dragTarget = null;
}
My approach would be to keep a track of whether mouseDownEssence() was called. And if not, call it before proceeding further. This approach would work somewhat as below. It would work differently for asynchronous functions but mouseDownEssence() seems to be a synchronous function.
let isMouseDownEssenceCalled = false;
function mouseDownEssence() {
isMouseDownEssenceCalled = true;
...
}
function mouseUpEssence() {
if (!isMouseDownEssenceCalled) {
mouseDownEssence()
}
...
isMouseDownEssenceCalled = false;
}

element.childElementCount is return incorrect value

I want to select element have no child.
This is js code
var elements = document.body.getElementsByTagName("*");
elements.each(function(element) {
if (element.childElementCount == 0) {
element.addEventListener(
"click",
function() {
makeBorder(selectedElement, false);
selectedElement = element;
makeBorder(element, true);
},
false
);
} else {
element.removeEventListener("click", null, false);
element.removeEventListener("onclick", null, false);
}
});
From link "m.naver.com", class["grid1 id_cui_cupid_news _MM_AIRS_CONTAINER"]'s real childElementCount is not 0.
But in code, childElementCount is 0.
I'm running this code in android webview.
How can I get the element that has no child?
reference : Select element inside WebView and get details
[[[[[[[ Edited ]]]]]]
Object.prototype.each = function(fn, bind) {
for (var i = 0; i < this.length; i++) {
if (i in this) {
fn.call(bind, this[i], i, this);
}
}
};
var selectedElement = null;
document.getElementsByTagName("*").addEventListener("click", function(e) {
if(e.target && e.target.childElementCount == 0) {
makeBorder(selectedElement, false);
selectedElement = element;
makeBorder(element, true);
}
});
// here
var makeBorder = function(element, selected) {
if (element) {
element.style.cssText = selected ? 'background: #CDEA90;' : '';
}
};

… is not a function in javascript {custom code}

please, could somebody tell me, what he heck I am doing wrong in my syntax?
The problem starts in the statement this.form.onsubmit, where I get this.initData is not a function.
Thanks.
var Contact_Form = function(element){
this.form = element;
this.errors = new Array();
this.invalid = new Array();
this.inSent = false;
this.name = new String();
this.email = new String();
this.message = new String();
this.initData = function()
{
this.name = this.getElementValue('contact-name');
this.email = this.getElementValue('contact-email');
this.message = this.getElementValue('contact-message');
}
this.form.onsubmit = function(event)
{
event.preventDefault();
this.initData();
if(this.verifyData())
this.send();
}
this.verifyData = function()
{
if(!this.isNameLength())
this.setError('name', 'Zadejte, prosím, jméno dlouhé maximálně 30 znaků.');
if(this.isProperEmail())
{
if(!this.isEmailLength())
this.setError('email', 'Váš e-mail smí obsahovat maximálně 50 znaků.');
}
else
this.setError('email', 'Zadejte, prosím, email v korektním formátu.');
if(!this.isMessageLength())
this.setError('name', 'Zadejte, prosím, zprávu v rozsahu 1-999 znaků.');
this.doInvalidFields();
if(0 == this.errors.length)
return true;
return false;
}
this.doInvalidFields = function()
{
if(this.invalid.length > 0)
{
for(var invalid in this.invalid)
this.getElement(invalid).setAttribute('aria-invalid', true);
}
}
this.setError = function(field, message)
{
this.errors.push(message);
this.invalid.push(field);
}
this.getElementValue = function(element) {
return this.getElement(element).value;
}
this.getElement = function(element) {
return document.getElementById(element);
}
this.getElementName = function() {
return this.getElement('contact-name');
}
this.getElementEmail = function() {
return this.getElement('contact-email');
}
this.getElementMessage = function() {
return this.getElement('contact-message');
}
this.isNameLength = function(){
return this.isLength(this.name, 1, 30);
}
this.isEmailLength = function(){
return this.isLength(this.email, 1, 50);
}
this.isMessageLength = function(){
return this.isLength(this.email, 1, 999);
}
this.isProperEmail = function() {
return this.email.match(/^(?:\w){1,100}#{1}(?:\w){1,100}(?:.){1}(?:\w){1,10}$/ig);
}
this.isLength = function isLength(string, _min, _max) {
if(string.length >= _min && string.length <= _max)
return true;
return false;
}
}
window.onload = function()
{
new Contact_Form(document.forms[0]);
}
The problem is that this is not inherited, and has a different value inside each function.
Then, use
var Contact_Form = function(element){
/* ... */
var that = this;
/* Here this===that */
this.form.onsubmit = function(event)
{
/* Here this===that.form */
event.preventDefault();
that.initData();
if(that.verifyData())
this.send();
}
/* ... */
}
this is referring to the form in the onsubmit handler. You could assign this to a local variable, or bind the handler to the correct this with Function.prototype.bind, ie:
this.form.onsubmit = function(event) {
event.preventDefault();
this.initData();
if(this.verifyData())
this.send();
}.bind(this)
or with jQuery.proxy
this.form.onsubmit = $.proxy(function(event) {
event.preventDefault();
this.initData();
if(this.verifyData())
this.send();
}, this);
Both examples are forcing the this context of the function to be the instance of a Contact_Form whenever the handler is called

attach event in loop, javascript

I know this is a "classic" and I already tried to read different explanatory articles on this subject, but I still manage to do it wrong somehow. I am talking about adding event handlers and functions in a javascript loop.
Here is my code with problems (it's a suggest-box / auto complete)
function autoCompleteCB(results) {
document.getElementById('autocom').innerHTML = '';
if (results.length == 0) {
document.getElementById('autocom').style.display = 'none';
} else {
document.getElementById('autocom').style.display = 'block';
var divholders = [];
for (var i = 0; i < results.length; i++) {
divholders[i] = document.createElement('div');
divholders[i].style.width = '350px';
var divrestext = document.createElement('div');
divrestext.className = 'autocom0';
divrestext.innerHTML = results[i][0];
divholders[i].appendChild(divrestext);
var divrestype = document.createElement('div');
divrestype.className = 'autocom1' + results[i][1];
divrestype.innerHTML = results[i][1];
divholders[i].appendChild(divrestype);
divholders[i].attachEvent('onmouseover', (function(i) { return function() { divholders[i].style.backgroundColor='#266699'; }; })(i));
divholders[i].attachEvent('onmouseout', (function (i) { return function() { divholders[i].style.backgroundColor = '#F5F5F5'; }; })(i));
document.getElementById('autocom').appendChild(divholders[i]);
}
}
}
It is (of course) the attachevent lines that do not work. This part of javascript is so weird/tricky :) Can a kind expert help me fix those two lines?
This is a half-way fix (I think(:
function bindEvent(element, type, listener) {
if (element.addEventListener) {
element.addEventListener(type, listener, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, listener);
}
}
function autoCompleteCB(results) {
document.getElementById('autocom').innerHTML = '';
if (results.length == 0) {
document.getElementById('autocom').style.display = 'none';
} else {
document.getElementById('autocom').style.display = 'block';
var divholders = [];
for (var i = 0; i < results.length; i++) {
divholders[i] = document.createElement('div');
divholders[i].style.width = '350px';
var divrestext = document.createElement('div');
divrestext.className = 'autocom0';
divrestext.innerHTML = results[i][0];
divholders[i].appendChild(divrestext);
var divrestype = document.createElement('div');
divrestype.className = 'autocom1' + results[i][1];
divrestype.innerHTML = results[i][1];
// BIND THE EVENTS
divholders[i].appendChild(divrestype);
document.getElementById('autocom').appendChild(divholders[i]);
}
}
}
It looks like this now, but still no "action"
function autoComplete() {
var ss = document.getElementById('txbkeyword').value;
if (ss.length > 0) { CSearch.SearchAutoComplete(ss, 3, autoCompleteCB); }
else { document.getElementById('autocom').style.display = 'none'; }
}
function bindEvent(element, type, listener) {
if (element.addEventListener) {
element.addEventListener(type, listener, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, listener);
}
}
function autoCompleteCB(results) {
document.getElementById('autocom').innerHTML = '';
if (results.length == 0) {
document.getElementById('autocom').style.display = 'none';
} else {
document.getElementById('autocom').style.display = 'block';
var divholders = [];
for (var i = 0; i < results.length; i++) {
divholders[i] = document.createElement('div');
divholders[i].style.width = '350px';
var divrestext = document.createElement('div');
divrestext.className = 'autocom0';
divrestext.innerHTML = results[i][0];
divholders[i].appendChild(divrestext);
var divrestype = document.createElement('div');
divrestype.className = 'autocom1' + results[i][1];
divrestype.innerHTML = results[i][1];
(function (i) {
bindEvent(divholders[i], 'mouseover', function () {
divholders[i].style.backgroundColor = '#266699';
});
bindEvent(divholders[i], 'mouseout', function () {
divholders[i].style.backgroundColor = '#F5F5F5';
});
})(i);
divholders[i].appendChild(divrestype);
document.getElementById('autocom').appendChild(divholders[i]);
}
}
}
One possibility is because attachEvent is IE-specific. You'll have to use attachEventListener in many other browsers.
And, to use the "proper" method for the current browser, you'll need to feature-detect them (snippet from MDN):
if (el.addEventListener){
el.addEventListener('click', modifyText, false);
} else if (el.attachEvent){
el.attachEvent('onclick', modifyText);
}
You can also create a function to aid in this:
function bindEvent(element, type, listener) {
if (element.addEventListener) {
element.addEventListener(type, listener, false);
} else if (element.attachEvent) {
element.attachEvent('on' + type, listener);
}
}
Then, in place of these 2 lines:
divholders[i].attachEvent('onmouseover', (function(i) { return function() { divholders[i].style.backgroundColor='#266699'; }; })(i));
divholders[i].attachEvent('onmouseout', (function (i) { return function() { divholders[i].style.backgroundColor = '#F5F5F5'; }; })(i));
...use the function to bind your handlers (skipping the on in the event type argument):
(function (i) {
bindEvent(divholders[i], 'mouseover', function () {
divholders[i].style.backgroundColor = '#266699';
});
bindEvent(divholders[i], 'mouseout', function () {
divholders[i].style.backgroundColor = '#F5F5F5';
});
})(i);
You could also just enclose the <div>:
(function (div, i) {
bindEvent(div, 'mouseover', function () {
div.style.backgroundColor = '#266699';
});
bindEvent(div, 'mouseout', function () {
div.style.backgroundColor = '#F5F5F5';
});
})(divholders[i], i);
Try this:
divholders[i].attachEvent('onmouseover', this.style.backgroundColor='#266699');

Exit Popup not working

I'm trying to get an exit popup to work. When user closes browser, it asks them if they want to stay or not, and in the background, it starts to redirect already.
This code works in Firefox, but not in Chrome and Opera.
In Chrome, the popup appears but no redirect happens.
In Opera, the popup doesn't appear at all.
function DisableExitTraffic() {
PreventExitSplash = true;
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
}
else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
function addClickEvent(a, i, func) {
if (typeof a[i].onclick != 'function') {
a[i].onclick = func;
}
}
theBody = document.body;
if (!theBody) {
theBody = document.getElementById("body");
if (!theBody) {
theBody = document.getElementsByTagName("body")[0];
}
}
var PreventExitSplash = false;
var LightwindowOpening = false;
function DisplayExitSplash() {
if (PreventExitSplash == false) {
window.scrollTo(0, 0);
window.alert(exitsplashalertmessage);
PreventExitSplash = true;
document.location.href = RedirectUrl;
return exitsplashmessage;
}
}
var a = document.getElementsByTagName('A');
for (var i = 0; i < a.length; i++) {
if (a[i].target !== '_blank') {
addClickEvent(a, i, function() {
PreventExitSplash = true;
});
}
else {
addClickEvent(a, i, function() {
PreventExitSplash = false;
});
}
}
disablelinksfunc = function() {
var a = document.getElementsByTagName('A');
for (var i = 0; i < a.length; i++) {
if (a[i].target !== '_blank') {
addClickEvent(a, i, function() {
PreventExitSplash = true;
});
}
else {
addClickEvent(a, i, function() {
PreventExitSplash = false;
});
}
}
}
addLoadEvent(disablelinksfunc);
disableformsfunc = function() {
var f = document.getElementsByTagName('form');
for (var i = 0; i < f.length; i++) {
if (!f[i].onclick) {
f[i].onclick = function() {
if (LightwindowOpening == false) {
PreventExitSplash = true;
}
}
}
else if (!f[i].onsubmit) {
f[i].onsubmit = function() {
PreventExitSplash = true;
}
}
}
}
addLoadEvent(disableformsfunc);
window.onbeforeunload = DisplayExitSplash;
var exitsplashalertmessage = '>>> W A I T ! <<<\n\nCongratulations!\nYour IP-address is selected, you could be a winner\n';
var exitsplashmessage = '>>> CONGRATULATIONS <<<\n\nClick the **CANCEL** button to select your prize!\n';
var RedirectUrl = 'http://google.com';
So you want to redirect the user inside an onbeforeunload event.
It looks like this answer could help you.
Code snippet:
window.onbeforeunload = function(){
location.assign('http://www.google.com');
return "go to google instead?";
}
You probably won't ever get exactly what you want, but you'll at least display a prompt, and if the user clicks on OK, it should redirect.
The easiest, and for everyone most satisfying answer to this question is: don't do that! If a user closes a browser that is the most powerful expression of "i really don't want to stay anymore" so why ask them again?
The only thing worse on the internet are those annoying sites where you click the back button and can't leave the page.
So please don't do such evil evil things with programming.

Categories

Resources