서명 팁이 반응이 좋아서 (애드온까지 만들어지다니 신나요~) 이번에는 후원금액을 선택 받는 팁을 공개해봅니다.

(물론 이번에도 국민 게시판ㅋ 스케치북 게시판을 이용합니다.)

 

아마 여러분은 둘 중 하나를 선택하실지 모르겠습니다.

- 라디오 버튼을 이용해서 후원자에게 객관식으로 선택지를 주거나 (예. 1만원, 2만원, 3만원, 5만원),

- 텍스트 입력폼을 이용해 후원자에게 주관식으로 직접 입력하게 하는 방식이겠죠.

 

이번에 소개해드리는 팁은 쉽게 말해, 객관식과 주관식을 섞는 방식입니다.

예컨대, 저는 게시판 쓰기 화면에서 다음과 같이 나오게 했습니다.

01.png

 

여기서 마지막 버튼('직접 입력')을 누르면 다음과 같이 됩니다.

02.png

이렇게 후원자가 후원금액을 직접적으로 입력할 수도 있게 한 거죠.

 

뿐만 아니라 input 이벤트를 이용해서 최소 금액('첫 번째 버튼에 표시된 금액')보다 후원금액이 적게 입력되는 경우에는 다음과 같이 메시지가 바뀌어서 출력됩니다.

03.png

 

그리고 이 상태에서 입력창을 벗어나려 하면 (천 단위로 콤마를 찍어줌과 동시에) 입력된 금액 부분을 전체 선택(하이라이트) 시키고 커서를 돌아오게 합니다. 아울러 메시지도 바뀌게 되죠.

04.png

 

또한, 엔터를 누르거나 등록 버튼을 클릭해서 입력 내용을 전송하려 하는 경우에는, 화면상에는 특정 숫자가 입력되어 있어도 실제로는 입력값이 초기화되어 입력 불가 경고창이 뜨게 됩니다.

05.png

 

하지만 최소금액 이상의 후원금액이 입력되면 메시지도 본래대로 (사용자정의 설명문 형태로) 돌아오고, 입력 값도 무사히 서버에 전송이 가능해집니다.

06.png

 

객관식 버튼을 눌렀을 때 후원금액이 별탈없이 전송되는 건 물론입니다.^^

07.png

 

... 그러면 이제 그 방법과 (다소 후줄근한ㅋ) 소스를 알려드리겠습니다.

 

1. 사용자 정의를 만들어줍니다.

- 저는 '사용자 정의 이름'을 'donation'으로 했습니다.

- '입력항목 이름'은 위 그림에서처럼 '후원금액'으로 했구요.

- '형식'은 '단일 선택(radio)', 기본값은 '5000, 10000, 20000, 30000, 50000, etc'로 했습니다.

- '설명'은 다음과 같이 했습니다. '후원회원으로 CMS를 통한 매월 약정액의 후원금 자동이체에 동의합니다.'

- 기본값의 구분은 쉼표로 하는 게 기본인데, 제 경우엔 쉼표와 띄어쓰기를 병행했습니다. 혹시 모르니 여러분도 가급적이면 띄어쓰기에 유의해주세요.

 

2. write_form.html 파일 수정

- <tr loop="$extra_keys=>$key,$val">에 속해 있는 <td> 태그를 찾고 아래처럼 바꿔주면 됩니다.

    <td cond="$val->eid!='donation'">{$val->getFormHTML()}</td>
    <td cond="$val->eid=='donation'">
        <style>
            .exForm li{display:inline-block;line-height:26px;cursor:pointer}
            #donation_etc{display:none;margin-top:10px;vertical-align:middle;}
            #donation_etc input{width:83px;height:26px;font-size:12px;}
        </style>
        <ul>
        {@
            $val_array = explode(', ',$val->default);
            $val_value = implode('',$oDocument->getExtraEidValue($val->eid));
        }
            <li loop="$n=0;$n<count($val_array);$n++">
            <!--@if($n+1!=count($val_array))-->
                <input name="extra_vars{$val->idx}" class="radio" id="extra_vars{$val->idx}-100{$n+1}" type="radio" value="{$val_array[$n]}">
                <label for="extra_vars{$val->idx}-100{$n+1}">월 {number_format($val_array[$n])}원</label>
            <!--@else-->
                <input name="extra_vars{$val->idx}" class="radio" id="extra_vars{$val->idx}-100{$n+1}" type="radio" value="">
                <label for="extra_vars{$val->idx}-100{$n+1}">직접 입력</label>
                <span id="donation_etc">
                    월 <input type="tel" id="etc_val" class="itx" value="<!--@if(!in_array($val_value,$val_array))-->{number_format($val_value)}<!--@end-->" />원
                </span>
            <!--@end-->
            </li>
        </ul>
        <p>{$val->desc}</p>
        <script>
        jQuery(document).ready(function(){
            var etc = jQuery('#donation_etc'),
                li_last = etc.parent('li'),
                para = li_last.parent('ul').next('p'),
                pText = li_last.parent('ul').next('p').text(),
                li_rest = li_last.parent('ul').children('li').not(li_last),
                first_val = Number(li_last.parent('ul').children('li:first').children('input.radio').val()),
                etc_btn = li_last.children('input'),
                etc_val = jQuery('#etc_val'),
                donated = '{$val_value}',
                msg1 = '최소 5천원 이상의 후원을 받고 있습니다.',
                msg2 = ' 후원금액 재입력을 부탁드립니다.';

            if(donated==0) etc_val.val('');
            if(donated!=''&&donated!=0){
                etc_btn.val(donated.replace(/[^0-9]/g,''));
                etc_btn.prop('checked',true);
                etc.show();
                li_rest.children('input').each(function(){
                    if(jQuery(this).val()==donated){
                        jQuery(this).prop('checked',true);
                        etc_val.val('');
                        etc.hide();
                    }
                });
            }

            etc_val.on({
                input: function(){
                    etc_val.val(etc_val.val().replace(/[^0-9,]/g,'').replace(/(^0+)/,''));
                    etc_btn.val(etc_val.val().replace(/[^0-9]/g,''));
                    if(etc_btn.val()!=0&&etc_btn.val()<first_val) {
                        etc_btn.val('');
                        para.text(msg1);
                    } else {
                        etc_btn.val(etc_val.val().replace(/[^0-9]/g,''));
                        para.text(pText);
                    }
                },
                blur: function(){
                    if(li_rest.data('mouseDown')!=true){
                        etc_btn.val(etc_val.val().replace(/[^0-9]/g,''));
                        etc_val.val(etc_val.val().replace(/[^0-9]/g,'').replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'));
                        if(etc_btn.val()!=0&&etc_btn.val()<first_val) {
                            etc_val.select();
                            etc_btn.val('');
                            para.text(msg1+msg2).hide().fadeIn(1000);
                            etc_val.focus();
                        }
                    }
                }
            });

            li_last.on('click',function(){
                etc_btn.prop('checked',true);
                etc.show();
                etc_val.focus();
                etc_val.val(etc_val.val().replace(/[^0-9]/g,'').replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'));
                if(etc_val.val().replace(/[^0-9]/g,'')!=0&&etc_val.val().replace(/[^0-9]/g,'')<first_val) {
                    etc_val.select();
                    etc_btn.val('');
                    para.text(msg1+msg2).hide().fadeIn(1000);
                }
                return false;
            });
            etc_btn.on('focus',function(){
                etc_btn.prop('checked',true);
                etc_val.focus();
            });

            li_rest.on('mousedown',function(e){
                li_rest.data('mouseDown',true);
            });
            li_rest.on('mouseup',function(e){
                li_rest.data('mouseDown',false);
            });
            li_rest.on('click',function(){
                jQuery(this).children('input').prop('checked',true);
                etc.hide();
                para.text(pText);
            });
        });
        </script>
    </td>

 

3. _read.html 파일 수정

- 쓰기 화면에서 입력된 값은 기본적으로 숫자로만 저장되어 전송됩니다.

- 만약 출력 화면에서는 천단위로 콤마를 찍어주고 싶다면, _read.html을 수정하시면 됩니다.

- 월 정기 후원인 경우에는 숫자 앞뒤로 '월'과 '원'을 붙여줘야겠죠?

- 먼저, 다음 부분을 찾습니다.

<td cond="$val->eid!='rating'">{$val->getValueHTML()}</td>
< td cond="$val->eid=='rating'" class="rating"><span class="starRating" title="{$val->getValueHTML()}{$lang->score}"><span style="width:{$val->getValueHTML()*10}%">{$val->getValueHTML()}</span></span></td>

- 이 부분은 모두 두 부분이 있습니다.  '스킨 설정 > 본문 일반 설정 > 확장변수 위치'가 '본문 안에(기본)'인 경우엔 아래쪽 table, '제목 아래'인 경우엔 윗쪽 table이 될 겁니다.

- 이 두 부분을 모두 다음과 같이 바꿔줍니다.

<td cond="$val->eid!='rating'&&$val->eid!='donation'">{$val->getValueHTML()}</td>
<td cond="$val->eid!='rating'&&$val->eid=='donation'">월 {number_format(str_replace(',','',$val->getValueHTML()))}원</td>
<td cond="$val->eid=='rating'&&$val->eid!='donation'" class="rating"><span class="starRating" title="{$val->getValueHTML()}{$lang->score}"><span style="width:{$val->getValueHTML()*10}%">{$val->getValueHTML()}</span></span></td>

 

이상입니다.

실제 제가 사용하는 코드와 달리 공개용 팁을 위해 코드를 다시 수정을 했는데요. 혹시라도 실수가 있을지 모릅니다. 안 되는 부분 남겨주시면 다시 살펴보고 일러드리도록 하겠습니다.

윤삼

profile
아무래도 중급 초반 수준의 코딩 오타쿠인 것 같습니다.
  • profile
    히야, 후원금이 아니더라도 사용자 정의 폼을 다양한 방법으로 활용할 수 있겠네요.

    안그래도 특정 항목을 선택했을 때 추가항목을 입력하도록 만들고 싶을 때 아주 유용하겠네요.

    그나저나 write_form.html 파일이나 _read.html 파일을 수정하면 스케치북 스킨을 쓰는 다른 게시판에도 영향이 미치겠죠?
  • profile profile
    네, 안 그래도 객관식+주관식 혼합이어서 다른 쓸모가 있겠지 싶었어요ㅎㅎ
    다른 게시판에 대한 영향은 td 태그의 cond 속성에 mid값을 조건 걸어주면 될 것 같아요.
    그게 아니면 스크립트를 잘 짜야 할 텐데 제 실력이 부족해요 ;-p
  • profile profile
    아참, mid 값을 정해주면 되는거였군요.

    좋은 팁 감사합니다!
  • profile
    윤삼님! 정말 멋지세요~ 저도 후원가입신청서 만들어야 했는데 암담했는데 하나하나 좋은 정보들이 올라와 기쁨니다. 한가지 가능하시다면 후원회원가입시 CMS신청 받을시 요즘은 필수동의 2가지를 받아야 하는데 동의 체크를 필수로 해야지만 전송될 수 있도록 하는건 구현이 안될까요? ㅎㅎ
  • profile profile
    아, 그러면 그것도 조만간 정리해서 올려보도록 할게요~
  • profile profile
    내용을 다시 읽어보니 동의 체크 '필수' 여부 문제였군요.

    그거라면 사용자 정의에서
    - '형식'을 '다중선택(checkbox)'로 하시고,
    - '필수항목'은 '예'를 선택,
    - '기본값'은 '동의함' 정도로 하시면 될 것 같네요.
  • profile profile
    아! 댓글 읽어보니 동의함 하나만 하면 되겠군요 ㅎㅎ 감사합니다.
  • profile
    실제로 자동이체 구현하려면 어떻게 하는 건가요?
  • profile profile
    앗, 이 경우엔 cms 회원을 모집하고 별도로 관리룰 하는 거여서 웹상에서 자동이체 시스템을 구현하는 쪽은 저도 잘 모르겠어요;;
  • profile profile
    앗;ㅁ; 별도 프로그램이 있으신거군요 ㅠ
  • profile profile
    저희는 유기견보호소후원을 위한 후원금을 무통장으로 받고 있는데요. 정기적으로 자동으로 후원요청이 있어서 추후 CMS운영도 고려하고 있는데요.
    대략 어떤 과정으로 하는건지 알 수 있을까요 ?

    지금 서명 받고 금액 신청하는 곳에서 CMS업체에게 전달할 모든 정보가 1페이지에 다 모아져서 그걸 CMS업체측에 전달하면 자동출금이 시작되게 되는 구조인가요 ?
  • profile profile
    제가 담당자가 아니어서 자세한 절차는 잘 모르지만 대략적으로는 그런 걸로 알고 있어요.
    지금은 출력화면을 pdf로 저장해서 담당자에게 전달해주고 있는데요, 이 참에 저도 자세히 좀 알아봐야겠네요.
  • profile profile
    아래 웹지기님 댓글에서처럼 cms업체에 후원신청서 자료를 넘기면 되는 것 같더라구요. 별도 프로그램을 사용하는지는 확실하지 않아요. 기회될 때 담당자한테 자세히 물어보도록 할게요~
  • profile profile
    개인이 프로그램을 만든다고 남의 통장에서 자동으로 돈을 빼올수는 없죠 ㅋ 금융기관이나 CMS서비스 업체에 요청을 해야 하는데 실제 이체 의사가 있는 예금주의 동의가 필요한 것입니다. 그래서 윤삼님께서 지금 알려주신 서명이나 금액 동의 등이 들어간 문서가 필요한 것이죠.
  • profile
    좋은 자료 뒤늦지만 감사하단 말씀 남깁니다.
  • ?
    우아 저도 한번 해봐야 되겠어요.^^ 감사합니다.
  • ?
    멋진 좋은 팁 감사했습니다.
    한가지 궁금해서 질문 남겨 봅니다.
    혹시 2개의 확장변수를 설정하는 방법이 있을까요.

    <td cond="$val->eid=='donation'">
    이부분을 두개 만들어 적용을 하니 스크립트에서 받아 주지를 않는지 정상적으로 작동을 하지를 않네요.
  • ? profile

    네, 스크립트가 확장변수 1개에만 대응하도록 되어 있어요.
    두 개 이상으로 하시려면 td 태그에 따로 클래스 속성을 줘서 .each() 등으로 잡아준다든지, html 부분도 소스 내에 중복되는 id 속성 같은 게 없도록 수정을 해줘야 할 거 같습니다.

  • profile ?
    답글 감사합니다.
    힘든 부분이군요^.^