Jump to content
  • 0

ООП в Java Script и циклы с использованием setTimeout(), спецэффекты


Yarik Voronov
 Share

Question

Предистория. Есть относительно большой проект, где предполагается использовать достаточное количество всплывающих div HTML элементов на одной странице. это могут быть диалоги, алерты, конфермы и прочая, прочая. Без определения классов и объектов тут не обойтись по многим причинам, одна из них самая существенная заключается в независимости экземпляров одного и того же класса друг от друга, но с одним интерфейсом. Ниже я приведу "вырезку" из кода (обособленную, рабочую build версию класса confirm).

Для удобного (эффектного) отображения конферма всплытие делается плавным (расширение сначала по ширине, затем по высоте от центра)

Собственно код

<!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>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Global Dialog Class</title>
<style type="text/css" media="screen">
.confirm {
font-family:Arial, Helvetica, sans-serif;
font-size:12px;
position:absolute;
top:50%;
left:50%;
background-color:#FFFFFF;
border: #36398F 2px solid;
z-index:30;
display:none;
overflow:hidden;
visibility:hidden;
}
.confirm H2 {
margin:0;
padding:2px 0px 5px 5px;
background-color:#FFF583;
border-bottom:1px solid #FFFFFF;
font-size:120%;
}
.confirm DIV.body {
clear:right;
max-height:300px;
overflow:auto;
padding:1%;
}
.confirm DIV.controls {
background-color:#F5F5F5;
border-top:1px solid #FFFFFF;
display:block;
padding:2px 2px 5px 2px;
text-align:center;
}
.confirm BUTTON.controls {
padding:2px 3px 2px 3px;
margin:0px 10px 0px 0px;
font-size:12px;
border:1px solid #36398F;
background-color:#ECE9D8;
width:100px;
}
</style>
</head>

<body>
<div id="confirmContaner" class="confirm">
<h2>
<img id="confirmCancelImg" src="/admin/icons/16x16/cancel.png" style="float:right; padding:2px 5px 0px;" alt="close" border="0"/>
<span id="confirmHead">Arguments.callee</span>
</h2>
<div id="confirmBody" class="body">
Это может быть очень полезно для отладки. Нахождение прототипа класса изнутри активированного объекта тоже не представляет трудности - arguments.callee.prototype.
Arguments.callee также показывает метод, которым можно найти имя его метода, опять-таки простым использованием arguments.callee. Это не так полезно, поскольку
свойства у методов обычно не прикрепляются ни к объекту с именем метода, ни к объекту прототипа, поэтому ссылки на эти объекты обычно не требуется. Кроме того, не
существует надежного способа, которым метод находил бы объект с именем класса, к которому он принадлежит (за исключением прописывания ссылки вручную).
</div>
<div class="controls">
<button id="confirmCancel" type="button" class="controls" onclick="MyTestDialog.hideDialog();">Cancel</button>
<button id="confirmOk" type="button" class="controls">OK</button>
</div>
</div>
<script language="javascript" type="text/javascript">
var el = function (label) {return document.getElementById(label)};

var GlobalDialogClass = function () {
this.marginTop = 0;
this.Height = 0;
this.marginLeft = 0;
this.Width = 0;
this.hObj = el('confirmContaner');
this.hObj.body = el('confirmBody');
}

GlobalDialogClass.prototype = {
displayDialog: function () {
with (this.hObj.style) {
visibility = 'hidden';
marginTop = 0 + 'px';
marginLeft = 0 + 'px';
display = 'block';
width = 300 + 'px';
this.h = this.hObj.clientHeight;
this.w = this.hObj.clientWidth;
width = 0 + 'px';
height = 0 + 'px';
visibility = 'visible';
this.changeWidth(3);
}
},

changeHeight: function (y) {
this.Height +=y;
this.hObj.style.height = this.Height + 'px';
this.hObj.style.marginTop = -Math.round(this.Height/2) + 'px';
var tObj = this;
var tFunc = function () {tObj.changeHeight(8)};
if (this.Height < this.h) setTimeout(tFunc, 0); else {
this.hObj.style.height = this.h + 'px';
this.Height = 0;}
},

changeWidth: function (x) {
this.Width +=x;
this.hObj.style.width = this.Width + 'px';
this.hObj.style.marginLeft = -Math.round(this.Width/2) + 'px';
var tObj = this;
var tFunc = function () {tObj.changeWidth(8)};
if (this.Width <= this.w) setTimeout(tFunc, 0); else {
this.hObj.style.width = this.w + 'px';
this.changeHeight(3);
this.Width = 0;}
},

hideDialog: function () {
this.hObj.style.display = 'none';
}
}

MyTestDialog = new GlobalDialogClass();
</script>



<button type="button" class="controls" onclick="MyTestDialog.displayDialog()">Проверить</button>
</body>
</html>

Собствено проблема

Трабл заключается в следующих строчках кода:

var tObj = this;
var tFunc = function () {tObj.changeWidth(8)};
if (this.Width <= this.w) setTimeout(tFunc, 0);

Ибо для IE это смерть - циклические ссылки, потеря памяти. Для других браузеров не удается увеличить скорость раскрытия элемента, насколько я понял из-за того что используются циклические ссылки, под них выделяется немерено памяти и т.п. А clip: rect() не однозначно работает в IE с данным доктайпом, но и все равно потребуется таймаут для его изменения.

Собственно вопрос

1. Как изменить код, чтобы иметь возможность более широко изменять скорость раскрытия окна?

Я не жду готового кода, мне достаточно ссылки, совета на данную проблему. Допускаю возможность что и алгоритм следует перекроить.

Заранее спасибо.

З.Ы. На данный момент я перечитываю инфу про утечки памяти в IE.

Link to comment
Share on other sites

5 answers to this question

Recommended Posts

  • 0
Как изменить код, чтобы иметь возможность более широко изменять скорость раскрытия окна?

Вариант номер один: tObj.changeWidth(8) => tObj.changeWidth(18)

Ибо для IE это смерть - циклические ссылки, потеря памяти.

Действительно ли это является причиной утечкек памяти, или это предположение?

Link to comment
Share on other sites

  • 0

>AKS: Вариант номер один: tObj.changeWidth(8) => tObj.changeWidth(18)

+1 :) и самое главное работает, я думал поначалу div будет передвигаться/меняться скачками.

>>Yarik Voronov: Ибо для IE это смерть - циклические ссылки, потеря памяти.

>AKS: Действительно ли это является причиной утечкек памяти, или это предположение?

Собственно перечитав

http://webo.in/articles/habrahabr/18-solvi...r-memory-leaks/

http://www.bazon.net/mishoo/articles.epl?art_id=824

выяснил (уяснил), что основная особенность утечек памяти состоит в том, что в замыканиях присутствуют перекрестные ссылки JS-конструкции и DOM/ActiveX-объекта друг на друга. собственно в приведенном коде таких замыканий не обнаружил (может плохо искал?). потому это было предположение, вы правы AKS.

И также протестировал IE6 SP1 и FF2.0.0.13 на прирост памяти при выполнении этого кода. увидел, что прирост при первом прогоне и там и там равен около 10 КБ. при последующих прогонах не меняется. НО все-равно мне этот код с setTimeout() не нравиться, потому что если судить по материалу (http-ссылки выше) то первая временная ссылка на один и тот же объект (MyTestDialog) отсвобождается когда закончиться последняя итерации изменения ширины (по алгоритму начинаем с длины). а этих ссылок примерно 72 (300/8 + 280/8, принудительная очистка clearTimeout() никак не повлияла). это (замораживание в памяти переменных предидущего setTimeout) и обусловливает, на мой взгляд, прирост оперативки на 10 KБ. А вот тормаза с setTimeout('',0) (по идее должен работать как простой while/for?) видимо связаны со скоростью обработки конструкции (объект и ссылки на MyTestDialog) самим JS - интерпритатором. или?...

В настоящий момент я переделал и взял за основу window.setInterval(); Эффект такой же а вот память не растет (получается только 2-е временные ссылки на первоначальный объект).

var GlobalDialogClass = function () {
// без изменений
}

GlobalDialogClass.prototype = {
displayDialog: function () {
// без изменений
},

changeWidth: function () {
var tObj = this;
this.wFlag = setInterval(function () {tObj.cW(18)},1);
},

cH: function (y) {
this.Height +=y;
this.hObj.style.height = this.Height + 'px';
this.hObj.style.marginTop = -Math.round(this.Height/2) + 'px';
if (this.Height >= this.h) {
clearInterval(this.hFlag);
this.hObj.style.height = this.h + 'px';
this.Height = 0;
}
},

cW: function (x) {
this.Width +=x;
this.hObj.style.width = this.Width + 'px';
this.hObj.style.marginLeft = -Math.round(this.Width/2) + 'px';
if (this.Width >= this.w) {
clearInterval(this.wFlag);
this.hObj.style.width = this.w + 'px';
var tObj = this;
this.hFlag = setInterval(function() {tObj.cH(8)},1);
this.Width = 0;
}
},

hideDialog: function () {
// без изменений
}
}

Вариант номер один

А был еще? Я вот подумывал сделать вложенные циклы по такому типу: while (ширина!=искомой величине) {пустой цикл скажем на 1 млн итераций; меняем ширину;} но подумал: "Одно из двух - либо память, либо процессор..." память победила :)

Link to comment
Share on other sites

  • 0
А вот тормаза с setTimeout('',0) (по идее должен работать как простой while/for?) видимо связаны со скоростью обработки конструкции (объект и ссылки на MyTestDialog) самим JS - интерпритатором. или?...

Работает не так, как while/for. Функция будет выполнена тогда, когда внимание процессора не будет занято какими-то задачами, которые, возможно, могли быть поставлены в очередь в момент выполнения текущей функции или чуть раньше. Это легко обнаружить, если провести какой-нибудь эксперимент, вызывая alert, какую-нибудь тяжелую функцию или существенно меняя структуру документа. Соответственно ваша предполагаемая связь скорости и обработки каких-либо конструкций вряд ли существует. Все в основном упирается в работу по прорисовке изменений анимируемого блока.

А был еще?

Я видел, как используется Date для вычисления времени изменений (для т.н. спецэффектов) и коррекции параметров анимации. Также можно использовать один setInterval для всех экземпляров. Короче, наверно много чего можно выдумать, но наверняка это окажется не интересным при разработке чего-то простенького.

Я вот подумывал сделать вложенные циклы...

Не надо. :)

Link to comment
Share on other sites

  • 0

AKS, спасибо.

Короче, наверно много чего можно выдумать, но наверняка это окажется не интересным при разработке чего-то простенького.

На самом деле мне интересно все. За намек на date спасибо - буду искать. Буду смотреть уже готовые решения.

...при разработке чего-то простенького

И не совсем так просто :). В данном посте я привел рабочую "вырезку" из целого кода (то что волновало) А все в целом выглядит так. Каждый класс отдельный js файл.

Экземпляр ConfirmObjectClass

|

Сам ConfirmObjectClass (подгружает из xml, создает стили (html style) оформления своего класса, в т.ч. и DOM-элементы, инициирует интерфейс)

|

ConfirmInterfaceClass (осуществляет запрос на открытие/скрытие экземпляра, вставку/очистку рабочего контента, уведомляет всех "подписанных" участников ситуации о произошедшем событии, реализуя ObserverInterfaceClass)

|

ConfirmClobalInterfaceClass (реализует расположение равнозначных экземпляров класса в одной области видимости на html странице)

|

GlobalDialogClass (осуществляет непосредственно скрытие/открытие экземпляра, реализует "спецэффекты", осуществляет перемещение Dom-объектов экземпляра класса, который его расширяет, по экрану)

|

ObserverInterfaceClass (реализует слабое связывание компонентов)

А сам дизайн конечного DOM-объекта я оставляю на откуп дизайнерам - пусть и они что-нить придумают :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. See more about our Guidelines and Privacy Policy