Firefox에서 input 태그를 다이나믹하게 추가했을 경우 form의 element로 인식 못하는 경우 발견

회사에서 아주 예전에 작성된 HTML을 수정하다가 하루종일 고생한 경험담을 얘기하고자 한다. 결론을 얘기하면 원인은 XHTML 표준을 지키지 않아서 였는데, 이게 원인일 줄이야!!!

다음과 같은 스타일의 HTML 태그가 있다고 하자. 과거에 CSS를 잘 사용하지 않을 때는 form 태그의 기본 패딩값에 의해 테이블 레이아웃이 깨지는 것을 방지하기 위해서 내가 자주 사용하던 스타일이다.

[code html]
<table>
<tr><td>테이블 헤더</td><tr>
<form>
<tr><td>폼 요소들</td></tr>
</form>
</table>
[/code]

여기에다 자바스크립트로 폼에 input 태그를 하나 추가하고, input 박스에 값을 치고 submit 버튼을 누르면 IE에서는 input 박스에 입력된 값이 전송이 되지만, 왠일인지 Firefox에서는 새로 추가된 input 박스의 내용은 전송이 되지 않는거다.

정말 하루 종일 삽질하다가 발견한 원인, 그리고 해결방안은 다음과 같이 XHTML을 준수하는 스타일로 HTML을 코딩하는 거였다.

[code html]
<form>
<table>
<tr><td>테이블 헤더</td><tr>
<tr><td>폼 요소들</td></tr>
</table>
</form>
[/code]

표준을 지키는게 중요함을 다시 한번 느낀 하루였다.

PHP의 print_r 함수를 ASP로 구현해보자.

PHP에는 변수의 내용을 쉽게 볼 수 있는 print_r 이란 함수가 있다. 변수형태와 상관없이 변수명만 인자로 넘겨주면 변수에 저장된 내용을 재귀적으로 표시해 줘서 디버깅시 특히 요긴하게 쓸 수 있다. ASP에서는 안타깝게도 유사한 함수가 없어서 비슷한 기능을 하는 함수를 만들어 봤다.

<%  
Sub print_r(var)  
    Dim myPrint  

    Set myPrint = New clsPrint  
    myPrint.print var, 0  
End Sub  

Class clsPrint  
    Public Sub print(var, nIndent)  
        Dim nType, sTypeName  
          
        'Response.Write TypeName(var)  

        nType = VarType(var)  
        Select Case nType  
        Case 0:  
            Response.Write "(vbEmpty)"  
        Case 1:  
            Response.Write "(vbNull)"  
        Case 2:  
            Response.Write var & "(vbIngerger)"  
        Case 3:  
            Response.Write var & "(vbLong)"  
        Case 4,5:  
            Response.Write var & "(vbSingle)"  
        Case 6:  
            Response.Write var & "(vbCurrency)"  
        Case 7:  
            Response.Write var & "(vbDate)"  
        Case 8:  
            Response.Write var & "(vbString)"  
        Case 9:'vbObject  
            sTypeName = TypeName(var)  
            Select Case sTypeName  
            Case "ISessionObject": 'Session  
                print_session var, nIndent  
            Case "IRequest": 'Request  
                print_request var, nIndent  
            Case "IApplicationObject": 'Application  
                print_application var, nIndent  
            Case Else  
                Response.Write "Unimpleneted TypeName:" & sTypeName  
            End Select  
        Case 10:  
            Response.Write "(vbError)"  
        Case 11:  
            Response.Write var & "(vbBoolean)"  
        Case 12:  
            Response.Write "(vbDataObject)"  
        Case 13:  
            Response.Write "(vbDecimal)"  
        Case 14:  
            Response.Write "(vbByte)"  
        Case 8192,8204:  
            print_array var, nIndent + 1  
        Case Else:  
            Response.Write "VarType:" & nType  
        End Select  

        Response.Write vbCrLf  
    End Sub  

    Sub print_array(var, nIndent)  
        print_list "Array", var, nIndent  
    End Sub  

    Sub print_request(var, nIndent)  
        print_name "Request", nIndent  
        print_openbrace nIndent  

        print_dict "Request.Form", var.Form, nIndent + 1  
        print_dict "Request.QueryString", var.QueryString, nIndent + 1  
        print_dict "Request.Cookies", var.Cookies, nIndent + 1  
        print_dict "Request.ServerVariables", var.ServerVariables, nIndent + 1  

        print_closebrace nIndent  
    End Sub  

    Sub print_application(var, nIndent)  
        print_name "Application", nIndent  
        print_openbrace nIndent  

        print_dict "Application.StaticObjects", var.StaticObjects, nIndent + 1  
        print_dict "Application.Contents", var.Contents, nIndent + 1  

        print_closebrace nIndent  
    End Sub  

    Sub print_session(var, nIndent)  
        print_name "Session", nIndent  
        print_openbrace nIndent  

        print_dict "Session.StaticObjects", var.StaticObjects, nIndent + 1  
        print_dict "Session.Contents", var.Contents, nIndent + 1  

        print_closebrace nIndent  
    End Sub  

    Sub print_dict(sTypeName, var, nIndent)  
        Dim sKey  

        print_name sTypeName, nIndent  
        print_openbrace nIndent  

        For Each sKey In var  
            print_key sKey, nIndent + 1  
            print var(sKey), nIndent + 1  
        Next  

        print_closebrace nIndent  
    End Sub  

    Sub print_list(sTypeName, var, nIndent)  
        Dim vValue, nIndex  

        Response.Write sTypeName & vbCrLf  
        print_openbrace nIndent  

        nIndex = 0  
        For Each vValue In var  
            print_key nIndex, nIndent + 1  
            print vValue, nIndent + 1  
            nIndex = nIndex + 1  
        Next  

        print_closebrace nIndent  
    End Sub  

    Private Sub print_name(sName, nIndent)  
        print_indent nIndent  
        Response.Write sName & vbCrLf  
    End Sub  

    Private Sub print_key(sKey, nIndent)  
        print_indent nIndent  
        Response.Write "[" & sKey & "] => "  
    End Sub  

    Private Sub print_openbrace(nIndent)  
        print_indent nIndent  
        Response.Write "(" & vbCrLf  
    End Sub  

    Private Sub print_closebrace(nIndent)  
        print_indent nIndent  
        Response.Write ")" & vbCrLf  
    End Sub  

    Private Sub print_indent(nIndent)  
        Response.Write String(nIndent * 4, " ")  
    End Sub  
End Class  


a = "123"  
b = Split("1 2 3 4 5", " ")  
print_r a  
print_r b  
print_r Session  
'print_r Application  
'print_r Request  
%>   

위를 브라우져에서 실행해서 소스보기를 하면 다음의 결과를 얻을 수 있다.

123(vbString)
Array
    (
        [0] => 1(vbString)
        [1] => 2(vbString)
        [2] => 3(vbString)
        [3] => 4(vbString)
        [4] => 5(vbString)
    )

Session
(
    Session.StaticObjects
    (
    )
    Session.Contents
    (
    )
)

배열도 지원하고, Application, Session, Request 객체의 내용도 볼 수 있다. 자체적으로 쓰는 객체는 “Unimpleneted TypeName”이란 결과가 나올텐데, 이는 소스에다 해당 객체를 출력하는 부분을 추가해 주면 된다.

VBScript의 GetRef을 이용하여 간단한 이벤트 드리븐 프로그래밍하기

VBScript는 객체 지향적인 측면에서는 많이 부족한 언어다. 상속이나 다형성을 전혀 제공하고 있지 않아서 클래스를 만드는게 오히려 불현할 때가 더 많은 게 사실이다. 조금이나마 이런 단점을 보안해 줄 수 있는 함수가 하나 있는데 GetRef 이다.

GetRef는 인자로 넘긴 문자열에 해당하는 함수 포인터를 반환하는 기능을 하는 함수이다.  이 함수를 이용하여 VBscript에서 간단하게 이벤트 드리븐(event driven) 프로그래밍하는 좋은 예제가 있어서 소개할려고 한다.



VBScript doesn’t have an event implementation so if you fancy having features like attaching handlers which will respond to specific events on your object you can do it simply by using the GetRef function and a bit of “syntactic sugar”.


I’m using ASP in these examples cos it’s easy.


Example 1 – Simple Events

‘Create a handler
Function MyHandler()
Response.Write “Hello from the handler!”
End Function

‘Create an event
Dim OnLoad
Set OnLoad = GetRef(“MyHandler”)

‘Fire the event
OnLoad()


Here we’ve created a simple event which takes one handler function and fired the event which in turn has called the function we attached.


To turn this in to a more useful event system we can use an array for the OnLoad event variable thus…

‘Create some handlers
Function MyHandler1()
Response.Write “Hello from handler 1!”
End Function

Function MyHandler2()
Response.Write “Hello from handler 2!”
End Function

‘Create an event
Dim OnLoad
OnLoad = Array(GetRef(“MyHandler1”), GetRef(“MyHandler2”))

‘Fire the event
For Each handler In OnLoad
handler()
Next


Example 2 – Event Arguments


In most event implementations the event handlers take one argument, passed to them by the fired event, which contains things like the type of event and a reference to the object on which it was fired etc.

‘Create a handler which takes one argument
Function MyHandler(e)
Response.Write “Hello from the handler – i was called by ” & e
End Function

‘Create two events
Dim OnLoad
Set OnLoad = GetRef(“MyHandler”)

Dim OnUnload
Set OnUnload = GetRef(“MyHandler”)

‘Fire the events
OnLoad(“Load”)
OnUnload(“Unload”)


Wrapping it up


We’ve established we can do all the basics of events, now all we need to do is wrap it up in a few classes to make it usable.


First we need an Event class that we can instantiate for each event we want. This will have to expose an event arguments property and methods for attaching handlers and firing the event. It will also have to keep track internally of the attached handlers. Lets have a go…

Class clsEvent

‘An array to keep track of our handlers
Private aryHandlers()

‘Our event arguments object to be passed
‘to the handlers
Public EventArgs

Private Sub Class_Initialize()
ReDim aryHandlers(-1)
Set EventArgs = New clsEventArgs
End Sub

Private Sub Class_Terminate()
Set EventArgs = Nothing
Erase aryHandlers
End Sub

‘Method for adding a handler
Public Function AddHandler(strFunctionName)
ReDim Preserve aryHandlers(UBound(aryHandlers) + 1)
Set aryHandlers(UBound(aryHandlers)) = _
GetRef(strFunctionName)
End Function

‘Method for firing the event
Public Function Fire(strType, objCaller)
EventArgs.EventType = strType
Set EventArgs.Caller = objCaller
For Each f In aryHandlers
f(EventArgs)
Next
End Function

End Class


Next we need an EventArgs class for passing data about the event to the handlers. This just needs three properties; event type, caller and an arguments collection for event type specific things.

Class clsEventArgs

Public EventType, Caller, Args

Private Sub Class_Initialize()
Set Args = CreateObject(“Scripting.Dictionary”)
End Sub

Private Sub Class_Terminate()
Args.RemoveAll
Set Args = Nothing
End Sub

End Class


Next our class that has an event, in this case an OnLoad which fires after the object’s Load method is called. We’ll also create a few handlers and do a trial run.

Class MyClass

Public OnLoad

Private Sub Class_Initialize()
‘Setting up our event
Set OnLoad = New clsEvent

‘Adding an argument
OnLoad.EventArgs.Args.Add “arg1”, “Hello”
End Sub

Public Function Load()
Response.Write “loading the object here!<br />”

‘Firing the event
OnLoad.Fire “load”, Me
End Function

End Class

‘A couple of handling function for the events
Function EventHandler(e)
Response.Write “<h2>EventHandler</h2>”
Response.Write “<p>Event “”” & e.EventType & “”” fired by object
of type ” & TypeName(e.Caller) & “.</p>”
End Function

Function EventHandler2(e)
Response.Write “<h2>EventHandler2</h2>”
For Each x In e.Args
Response.Write x & “: ” & e.Args(x) & “<br />”
Next
End Function

‘instantiate the object, attach the handlers and call the load
Set myObj = New MyClass
myObj.OnLoad.AddHandler(“EventHandler”)
myObj.OnLoad.AddHandler(“EventHandler2”)
myObj.Load()


Event based programming reverses the responsibility for code execution within your program. In conventional procedural programming it would be the responsibility of the myObj class to make sure the two event handlers were fired when it’s Load method was called. By using an OnLoad event instead myObj doesn’t have to know anything about the environment in which its executing, it just blindly fires the event and any attached handlers will be called. In this way you can add additional functions which run when myObj’s Load method is called without modifying MyClass.


In more complex systems being able to add functionality with a minimum of intrusion into other parts of the system is a big bonus and event based programming is an easy way of achieving it.


출처: http://derek-says.blogspot.com/2006/10/simple-event-driven-programming-using.html