01.09.2008, 18:08
|
Интересующийся
|
|
Регистрация: 01.09.2008
Сообщений: 18
|
|
Электронные таблицы на JS
Здравствуйте!
Хочу сделать нечто вроде экселевской таблицы на JavaScript. Я сделал её, но работает медленно. Если взять таблицу размером (800х14) то её загрузка в IE требует 29 секунд. Я недоволен. Не хочется изобретать велосипед, может ктото делал такую вещь? Посоветуйте что нибудь. Могу скинуть свой код, там 205 строк.
|
|
01.09.2008, 18:57
|
Флудер
|
|
Регистрация: 25.07.2008
Сообщений: 1,271
|
|
скинь, мне интересно.
У меня генерация таблицы 800*14 в ФФ2 занимает 2100миллисекунды (визуально 3).
|
|
01.09.2008, 19:24
|
Новичок на форуме
|
|
Регистрация: 19.02.2008
Сообщений: 9,177
|
|
Сам не реализовывал, поэтому по слухам: большие таблицы сильно тормозят (в ИЕ?). Вариант решения: разбивать на много маленьких таблиц, следующих друг за другом.
|
|
01.09.2008, 20:24
|
Флудер
|
|
Регистрация: 25.07.2008
Сообщений: 1,271
|
|
Большик таблицы просто жесть как тормозят в ИЕ, но также они тормозят в Сафари (но меньше), потом идёт ФФ, и почти не тормозят в Опере (но в опере вылазит куча других проблем)...
|
|
02.09.2008, 09:33
|
Интересующийся
|
|
Регистрация: 01.09.2008
Сообщений: 18
|
|
вот мой код
/*
SpreadSheet.js
*/
Array.prototype.search = function(search){
var i=0;while(this[i++]!=search && i<this.length);
return this[i-1]!=search?false:i-1;
};
/*
ЯЧЕЙКА ТАБЛИЦИ
*/
function cell(){
this.r = 0;
this.c = 0;
this.parent = null;
this.value = '';
this.borderColor = '#7F9DB9';
this.borderWidth = '1';
this.defaultStyle = function(){
this.borderColor = '#7F9DB9';
this.borderWidth = '1';
};
this.activeStyle = function(){
this.borderColor = '#000000';
this.borderWidth = '3';
};
this.setAutoCompleter = function(url){
/*var td = this.findTableCell(this.r,this.c);
var input = td.getElementsByTagName('INPUT')[0];
var acDiv = document.createElement("DIV");
acDiv.className = 'ac';
td.appendChild(acDiv);
this.AutoCompleter = new AutoCompleter(acDiv,input,url);
_onkeydown = input.onkeydown;
input.onkeydown = function(e){
if('block' == acDiv.style.display){}
else {_onkeydown();}
};*/
};
this.findTableCell = function(r,c){
var t = this.parent._obj.getElementsByTagName('TABLE');
return t[t.length-1].getElementsByTagName('TR')[this.r-1].getElementsByTagName('TD')[this.c-1];
};
}
/*
ТАБЛИЦА
*/
function sheet(obj){
return {
row:1,
col:1,
cells:[], // хеш массив ячеек
_obj:obj,
cell:function(r,c){
var rCount=0,cCount=0;
while(this.cells.length < r){
this.cells.push([]);
rCount = this.cells.length - 1;
while(this.cells[rCount].length < c){
this.cells[rCount].push(new cell());
cCount = this.cells[rCount].length - 1;
this.cells[rCount][cCount].r = rCount+1;
this.cells[rCount][cCount].c = cCount+1;
this.cells[rCount][cCount].parent = this;
}
}
return this.cells[r-1][c-1];
},
moveTo:function(r,c){
if(r<1 || c<1 || r>this.cells.length || c>this.cells[0].length)
return;
with(this){
cell(this.row,this.col).defaultStyle();
cell(r,c).activeStyle();
updateCell(this.row,this.col);
updateCell(r,c);
row=r;
col=c;
}
},
updateCell:function(r,c){
var t = obj.getElementsByTagName('TABLE');
row = t[t.length-1].getElementsByTagName('TR')[r-1];
cell = row.cells[c-1];
input = cell.getElementsByTagName('INPUT')[0];
input.style.borderWidth = this.cell(r,c).borderWidth;
input.style.borderColor = this.cell(r,c).borderColor;
if(''!=input.value && input.value!=this.cell(r,c).value)
this.cell(r,c).value = input.value;
input.value = this.cell(r,c).value;
input.focus();
},
show:function(){
t = [];
currenCell = {};
t.push('<div></div><br><div><table border="0">');
rCount = this.cells.length;
cCount = this.cells[0].length;
var tt= this
var OnKeyDown = function(e){
switch( keyCode(e) ){
case 37: tt.moveTo(tt.row,tt.col-1); break; // move left
case 38: tt.moveTo(tt.row-1,tt.col); break; // move up
case 39: tt.moveTo(tt.row,tt.col+1); break; // move right
case 40: tt.moveTo(tt.row+1,tt.col); break; // move down
}
};
for(r=0; r<rCount; r++){
t.push('<tr>');
for(c=0; c<cCount; c++){
currenCell = this.cells[r][c];
t.push('<td><input autocomplete="off" style="width:100%;border:'+ currenCell.borderWidth +'px '+currenCell.borderColor+' solid" value="' + currenCell.value + '"></td>');
}
t.push('</tr>');
}
t.push('</table></div>');
obj.innerHTML = t.join("\n");
var inputs = obj.getElementsByTagName('INPUT');
var inpCount = inputs.length;
for(i=0;i<inpCount;i++){
inputs[i].onclick= function(){
c = this.parentNode.cellIndex +1;
r = this.parentNode.parentNode.rowIndex +1;
tt.moveTo(r,c);
};
inputs[i].onkeydown = OnKeyDown;
}/**/
window.onunload = function(){tt.cleanUp(tt)};
},
addHeaders:function(labelsList){ // labelList format: {name1:isSortable,name2:isSortable}
var labels = [],h = [];
for(key in labelsList)
labels.push({name:key,isSortable:labelsList[key]});
var rCount = this.cells[0].length;
var t = this._obj.getElementsByTagName('DIV')[0];
var tw = this._obj.getElementsByTagName('TABLE')[0].clientWidth;
var dt = this._obj.getElementsByTagName('TABLE')[0].getElementsByTagName('TR');
h.push('<table cellspacing="0" cellpadding="0"><tr>');
var v,options=[];
for(i=0;i<rCount;i++){
h.push('<td>');
if(i<labels.length){
if(labels[i].isSortable){
h.push('<select style="background:#99CCFF;">');
h.push('<option>'+labels[i].name+'</option>');
for(j=0;j<this.cells.length;j++){
if(false===options.search(this.cells[j][i].value) && ''!=this.cells[j][i].value){
options.push(this.cells[j][i].value);
}
}
for(j=0;j<options.length;j++)
h.push('<option value="'+options[j]+'">'+options[j]+'</option>');
options=[];
h.push('</select>');
}else
h.push(labels[i].name);
}else{h.push(' ')}
h.push('</td>');
}
h.push('</tr></table>');
t.innerHTML = h.join("\n");
h = this._obj.getElementsByTagName('TABLE')[0].getElementsByTagName('TR')[0].getElementsByTagName('TD');
t = this._obj.getElementsByTagName('TABLE')[1].getElementsByTagName('TR')[0].getElementsByTagName('TD');
this._obj.getElementsByTagName('TABLE')[1].style.width = this._obj.getElementsByTagName('TABLE')[0].clientWidth;
for(i=0;i<h.length;i++)
t[i].style.width = h[i].clientWidth;
},
getValueList:function(collIndex){
var list = [],v;
for(i=0;i<this.cells.length;i++){
if(!list.toString().match(this.cells[i][collIndex].value)){
v = this.cells[i][collIndex].value;
list.push('<option value="' + v +'">' + v + '</option>');
}
}
return list.join('');
},
cleanUp:function(tt){
// избегаем утечку памяти в Microsoft Internet Explorer
for(i=0;i<tt.cells.length;i++){
for(j=0;j<tt.cells[0].length;j++){
tt.cells[i][j].parent=null;
if(tt.cells[i][j].AutoCompleter){
tt.cells[i][j].AutoCompleter = null;
}
}
}
var inp = document.getElementsByTagName('INPUT');
for(l=0;l<inp.length;l++){
inp[l].onclick = null;
inp[l].onkeydown = null;
}
}
};
}
function keyCode(e){
if (window.event) return window.event.keyCode;
else if (e) return e.which;
}
так его запускать
<html>
<head>
<!--<link type="text/css" href="style.css" rel="stylesheet">-->
<!--<script type="text/javascript" src="java.js"></script>-->
</head>
<body>
<!-- ЭЛЕКТРОННАЯ ТАБЛИЦА-->
<script type="text/javascript" src="SpreadSheet.js"></script>
<div id="SpreadSheet" align="center">
</div>
<script>
var b = new Date();
var s = new sheet(document.getElementById('SpreadSheet'));
s.cell(800,14);
s.show();
// s.cell(1,1).value = '11';
//s.updateCell(1,1);
//s.moveTo(1,1);
//s.cell(305,14);
//s.cell(1,1).setAutoCompleter('../helpdesk/trunk/ajaxDispecher.php?name=street');
s.addHeaders({'Адрес установки':1,'Дом':1,'Под':1,'Switch':1,'IP адрес':1,'serial sw':1,'модуль':1,'1 модуль':1,'2 модуль':1,'Заказан':1,'Подготовлен':1,'Выдан':1,'Установлен':1,'Пометки':1});
var e = new Date();
alert((e.getTime() - b.getTime())/1000);
</script>
<!-- КОНЕЦ ЭЛЕКТРОННОЙ ТАБЛИЦИ-->
</body>
</html>
Последний раз редактировалось gagagogo, 02.09.2008 в 09:40.
|
|
02.09.2008, 09:55
|
Флудер
|
|
Регистрация: 25.07.2008
Сообщений: 1,271
|
|
жесть... updateCell тормозная функция 117 милисекунд выполняется... с учётом того, что на одно передвижение она вызывается 2 раза получаются визуальные тормоза...
После профилировки обнаружил, что addHeaders занимает больше времени при создании таблицы, чем show
Оптимизировать и оптимизировать...
Советы:
- избавиться от всех getElementsByTagName (снести в инициализацию, одни раз их сделать, записать в свойства объекта "Электронная Таблица")
- не использовать .push, а использовать [index] = ...
- ты при генерации таблицы делаешь t.innerHTML = h.join("\n"); Я не помещаю строки в массив, а формирую сразу строку. В итоге получается быстрее, чем у тебя (хоть тесты и говорят, что в массив быстрее)...
|
|
02.09.2008, 12:53
|
Интересующийся
|
|
Регистрация: 01.09.2008
Сообщений: 18
|
|
ZoNT, спасибо за советы. Как думаешь если оптимизировать можно в 2 секунды уложиться ? Или может лучше сгенерировать таблицу на php, а к ней уже обработчики событий прикрутить? Или может лучше вообще отказаться от этой затеи и написать на java аплет? Если у тебя есть можеш свой вариант скинуть ?
|
|
02.09.2008, 12:54
|
Флудер
|
|
Регистрация: 25.07.2008
Сообщений: 1,271
|
|
Немного ускорил.
Но структура бредовая...
1) Зачем в каждую ячейку пихать инпут?
2) Зачем на каждый инпут(ячейку) вешать обработчик событий?
/*
SpreadSheet.js
*/
Array.prototype.search = function(search){
for(var i=0,l=this.length;i<l;i++)if(this[i]==search)return i;
return false;
}
/*
ЯЧЕЙКА ТАБЛИЦИ
*/
function cell(){
this.r = 0;
this.c = 0;
this.parent = null;
this.value = '';
this.borderColor = '#7F9DB9';
this.borderWidth = '1';
this.defaultStyle = function(){
this.borderColor = '#7F9DB9';
this.borderWidth = '1';
}
this.activeStyle = function(){
this.borderColor = '#000000';
this.borderWidth = '3';
}
this.setAutoCompleter = function(url){}
this.findTableCell = function(r,c){
var t = this.parent._obj.getElementsByTagName('TABLE');
return t[t.length-1].getElementsByTagName('TR')[this.r-1].getElementsByTagName('TD')[this.c-1];
}
}
/*
ТАБЛИЦА
*/
function sheet(obj){
return {
row:1,
col:1,
cells:[], // хеш массив ячеек
_obj:obj,
cell:function(r,c){
var rCount=0,cCount=0;
while(this.cells.length < r){
this.cells.push([]);
rCount = this.cells.length - 1;
while(this.cells[rCount].length < c){
this.cells[rCount].push(new cell());
cCount = this.cells[rCount].length - 1;
this.cells[rCount][cCount].r = rCount+1;
this.cells[rCount][cCount].c = cCount+1;
this.cells[rCount][cCount].parent = this;
}
}
return this.cells[r-1][c-1];
},
moveTo:function(r,c){
if(r<1 || c<1 || r>this.cells.length || c>this.cells[0].length)
return;
with(this){
cell(this.row,this.col).defaultStyle();
cell(r,c).activeStyle();
updateCell(this.row,this.col);
updateCell(r,c);
row=r;
col=c;
}
},
updateCell:function(r,c){
var input = this.Table.rows[r-1].cells[c-1].firstChild;
var C = this.cell(r,c);
input.style.borderWidth = C.borderWidth;
input.style.borderColor = C.borderColor;
if(''!=input.value && input.value!=C.value)
C.value = input.value;
input.value = C.value;
input.focus();
},
show:function(){
var t=[],i=0,currenCell;
t[i++] = '<table border="0">';
var rCount = this.cells.length;
var cCount = this.cells[0].length;
var tt = this;
var OnKeyDown = function(e){
e=e||event;
switch(e.keyCode||e.charCode){
case 37: tt.moveTo(tt.row,tt.col-1); break; // move left
case 38: tt.moveTo(tt.row-1,tt.col); break; // move up
case 39: tt.moveTo(tt.row,tt.col+1); break; // move right
case 40: tt.moveTo(tt.row+1,tt.col); break; // move down
}
}
t[i++] = '<tr>';
var h = this.Header.rows[0];
for(var c=0; c<cCount; c++){
currenCell = this.cells[0][c];
t[i++] = '<td style="width:'+h.cells[c].offsetWidth+'px;"><input autocomplete="off" style="width:100%;border:'+ currenCell.borderWidth +'px '+currenCell.borderColor+' solid" value="' + currenCell.value + '"></td>';
}
t[i++] = '</tr>';
for(var r=1; r<rCount; r++){
t[i++] = '<tr>';
for(var c=0; c<cCount; c++){
currenCell = this.cells[r][c];
t[i++] = '<td><input autocomplete="off" style="width:100%;border:'+ currenCell.borderWidth +'px '+currenCell.borderColor+' solid" value="' + currenCell.value + '"></td>';
}
t[i++] = '</tr>';
}
t[i++] = '</table>';
this._obj.lastChild.innerHTML = t.join("");
this.Table = this._obj.lastChild.firstChild;
this.Table.style.width = this.Header.offsetWidth;
var inputs = this._obj.getElementsByTagName('INPUT');
var inpCount = inputs.length;
for(var i=0;i<inpCount;i++){
inputs[i].onclick= function(){
c = this.parentNode.cellIndex +1;
r = this.parentNode.parentNode.rowIndex +1;
tt.moveTo(r,c);
};
inputs[i].onkeydown = OnKeyDown;
}
window.onunload = function(){tt.cleanUp(tt)};
},
addHeaders:function(labelsList){ // labelList format: {name1:isSortable,name2:isSortable}
var labels = [],h = [],k=0;
for(var key in labelsList)
labels.push({name:key,isSortable:labelsList[key]});
var rCount = this.cells[0].length;
var cCount = this.cells.length;
var t = this._obj;
h[k++] = '<div><table cellspacing="0" cellpadding="0"><tr>';
for(var i=0;i<rCount;i++){
h[k++] = '<td>';
if(i<labels.length){
if(labels[i].isSortable){
h[k++] = '<select style="background:#99CCFF;"><option>'+labels[i].name+'</option>';
var options=[];
for(var j=0;j<cCount;j++){
var v = this.cells[j][i].value;
if(false===options.search(v) && ''!=v){
options.push(v);
h[k++] = '<option value="'+v+'">'+v+'</option>';
}
}
h[k++] = '</select>';
}
else h[k++] = labels[i].name;
}
else h[k++] = ' ';
h[k++] = '</td>';
}
h[k++] = '</tr></table></div><br><div></div>';
t.innerHTML = h.join("");
this.Header = t.firstChild.firstChild;
},
getValueList:function(collIndex){
var list = [],v;
for(var i=0;i<this.cells.length;i++){
if(!list.toString().match(this.cells[i][collIndex].value)){
v = this.cells[i][collIndex].value;
list.push('<option value="' + v +'">' + v + '</option>');
}
}
return list.join('');
},
cleanUp:function(tt){
// избегаем утечку памяти в Microsoft Internet Explorer
for(var i=0;i<tt.cells.length;i++){
for(var j=0;j<tt.cells[0].length;j++){
tt.cells[i][j].parent=null;
if(tt.cells[i][j].AutoCompleter){
tt.cells[i][j].AutoCompleter = null;
}
}
}
var inp = document.getElementsByTagName('INPUT');
for(var l=0;l<inp.length;l++){
inp[l].onclick = inp[l].onkeydown = null;
}
}
};
}
Вызов addHeaders в html должен идти ПЕРЕД show()!!!
Последний раз редактировалось ZoNT, 02.09.2008 в 13:00.
|
|
02.09.2008, 12:59
|
Флудер
|
|
Регистрация: 25.07.2008
Сообщений: 1,271
|
|
Сообщение от gagagogo
|
ZoNT, спасибо за советы. Как думаешь если оптимизировать можно в 2 секунды уложиться ? Или может лучше сгенерировать таблицу на php, а к ней уже обработчики событий прикрутить? Или может лучше вообще отказаться от этой затеи и написать на java аплет? Если у тебя есть можеш свой вариант скинуть ?
|
Свой вариант скинуть не могу (так как он делался для коммерческих целей), но твой мы можем доработать до этих самых двух секунд...
Если на пхп таблица сгенерится быстрее, то это конечно лучший вариант...
|
|
02.09.2008, 13:32
|
Интересующийся
|
|
Регистрация: 01.09.2008
Сообщений: 18
|
|
Ага, учтя твои замечания думаю сделать так:
1 таблицу генерировать на пхп
2 сделать один input который будет бегать по ячейкам таблицы и менять значения в них.
3 на input повесить все события, и убрать объект cell - поидеи он тогда вообще не нужен будет
|
|
|
|