블로그 이미지
물결(Wave)
하고 싶은 것만 하며 살고 싶다

calendar

          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
가계부를 업데이트 하면서 적용해봤는데 나름 만족할만한 정도다.



테이블 셀 작업 샘플보기

[editableText.js]
/*
작성자: 물결(asrada2001.mireene.com)
작성일: 2007-05-29
*/

function createEditor(callback){
   var editor = new Editor();
   editor.callback = callback;

   try{
       document.addEventListener('click',clickEventHandler,false);
   }catch(e){
       document.attachEvent('onclick',clickEventHandler);
   }

   return editor;
}
function clickEventHandler(e){
   var evt = e || window.event;
   var target;

   if(evt.target){
       target = evt.target;
   }else{
       target = evt.srcElement;
   }

   if(target.className.search('editable') > -1){
       editor.editing(target);
   }
}

function Editor(){
   this.autosave = false;
   this.source = null;
   this.callback = null;
   this.container = null;
   this.frm = document.createElement('INPUT');
   this.frm.className = 'editor';
   this.frm.model = this;

   this.frm.onkeydown = function(e){
       var evt = e || window.event;

       if(evt.keyCode == 13){
           try{
               evt.preventDefault();
           }catch(ex){
               evt.returnValue = false;
           }

           this.model.complete();
       }
   }

   this.frm.onblur = function(e){
       if(this.model.autosave == true){
            this.model.complete();
        }else{
            this.model.cancel();
        }
   }
}

Editor.prototype.editing = function(obj){
   try{
       this.container = obj;

       if(obj.hasChildNodes() == false){
           obj.appendChild(document.createTextNode(''));
       }

       this.source = obj.firstChild;
       this.frm.value = this.source.nodeValue;
       this.frm.style.width = '100%';

       if(this.container.style.textAlign != undefined && this.container.style.textAlign != ''){
            this.frm.style.textAlign = this.container.style.textAlign;
        }else if(this.container.align != undefined && this.container.align != ''){
            this.frm.style.textAlign = this.container.align;
        }else{
            this.frm.style.textAlign = 'left';
       }

       this.container.replaceChild(this.frm,this.source);
       this.frm.focus();
       this.frm.value += '';
   }catch(e){
   }
}

Editor.prototype.cancel = function(){
   this.finish();
}

Editor.prototype.complete = function(){
   this.save();
   this.finish();

   if(this.callback instanceof Function){
       this.callback(this.container);
   }else{
   }
}

Editor.prototype.finish = function(){
   this.container.replaceChild(this.source,this.frm);
}

Editor.prototype.save = function(){
   this.source = document.createTextNode(this.frm.value);
}


모듈을 페이지에 로드시키고 에디터 객체를 생성하면 준비는 끝.
<script type="text/javascript" src="editableText.js"></script>
<script type="text/javascript">
var editor = createEditor(callback);

function callback(obj){
}
</script>


사용자가 문서의 텍스트 부분을 클릭하면 텍스트 노드를 담고있는 container 객체의 className 속성에 'editable' 가 있는지 확인한 후 에디터 객체(INPUT)에 텍스트를 전달하고 텍스트노드와 에디터를 바꿔치기한다.

에디터는 싱글턴(singleton)으로 하나의 에디터가 문서 전역에서 동작한다.

autosave 속성이 true로 설정되면 에디터에서 수정후 포커스가 다른 곳으로 이동하면 자동으로 수정한 내용이 원본 텍스트 노드에 그대로 적용이 되고 반대로 false면 원 상태로 되돌린다.
autosave를 적용하지 않으면 사용자는 데이터 수정 후 엔터키를 눌러야 수정한 내용이 반영된다.

callback 함수를 지정하면 save() 메서드를 실행 후 callback 함수를 호출한다.
xmlHttpRequest 객체를 사용할 경우 동적으로 DB의 내용을 수정 할 수 있다.

원래는 source를 <span>태그에만 할당 하려했으나 셀작업에 쓰는게 더 좋을 것 같아서 'edtiable'을 className으로 갖는 모든 객체로 수정하였다.
하지만 변경 후 맘에 안드는 것이, 원글 너비(offsetWidth)의 반영이다.
<span>,<div>등 block 객체에 edtiable 을 적용하면 원글의 사이즈에 맞게 에디터의 사이즈를 조절할 수 있는 반면 테이블의 셀(TD)에 적용 할 경우 테이블 셀 크기에 맞춰버리니 맞춤 사이즈는 포기할 수 밖에 없다....
방법을 아시는 분은 코멘트를 부탁...^^;


*주의*
Editor는 멀티라인 편집을 지원하지 않는다.
<span>,<td>,<div>,<p> 의 엘리먼트에서 오직 가장 처음에 위치한 텍스트 노드를 편집 할 수 있다.
멀티라인 편집을 위해 Editor 객체에 에디터를 input과 textarea 두개로 구분하여 childNodes.lenth로 멀티라인
여부를 판별한 후 해당 엘리먼트에 맞는 에디터를 쓰게끔 리펙토링을 시도하였으나,
editing() 메서드와 finish() 메서드에서 replaceChild를 쓸 수 없고,
텍스트 노드 사이의 태그를 검출해야 하며, 멀티라인의 경우 줄바꿈 처리를 하는 등 코드가 복잡해지는 관계로 단문 인라인 엘리먼트에 이 스크립트를 사용할 것을 당부하는 것으로 대신한다.

신고
posted by 물결(Wave)