링크를 클릭했는데, 페이지에 return false;가 찍혀나오는 경우가 있다.

소스

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
function test() {
if (true) {
return false;
}
}
</script>

<!-- 페이지에 return false;가 출력됨 -->
<a href="javascript:test();">테스트</a>

<!-- 대체 -->
<a href="javascript://" onClick="test();">테스트</a>

설명

a href 속성 안에 javascript 를 사용시 발생한다.
javascript 호출문인 onClick 을 이용하자.

전체 레코드 수와 현재 페이지 번호, 페이지에 표시되는 레코드의 수를 안다면 쉽게 구할 수 있다.

소스

1
2
3
4
5
6
7
var totalCount;
var currentPageNum;
var shownRowNum;

for (var i = 0, len = data.length; i < len; i++) {
var index = Number(totalCount) - (i + (currentPageNum - 1) * shownRowNum);
}

여담

매번 공식을 알아내는게 귀찮아서 포스팅

aaa.com 에서 bbb.com 의 세션을 jsonp 방식을 활용해서 생성하고 싶을 때,
유독 IE 에서만 세션 생성이 안되는 경우가 있다.

이는 P3P (Platform for Personal Preferences) 규약이 적용되어 세션을 가져오지 않는 것인데, 아래와 같은 방법으로 해결할 수 있다.

소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 세션을 만들어줄 서버에서 P3P 헤더를 설정 -->
<?php
header('P3P: CP="CAO PSA OUR"');
?>

<!-- 세션을 가지고 오는 클라이언트 요청-->
<script>
$.ajax({
url:'cross domain session create url',
type:'post',
data:{
token1 : 'token1',
token2 : 'token2'
},
dataType:'jsonp'
}).then(function (result) {

});
</script>

가장 빠른 배열 중복 제거 알고리즘

통계 사용시에 중복을 제거해야하는 경우가 있다.

소스

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
32
33
34
35
36
37
38
39
/*
@author http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/
*/

// prototype
Array.prototype.unique = function () {
var o = {},
i,
r = [];
var l = this.length;

for (i = 0; i < l; i++) {
o[this[i]] = this[i];
}

for (i in o) {
r.push(o[i]);
}

return r;
};

// function
var uniqueArray = function (arr) {
var o = {},
i,
r = [];
var l = arr.length;

for (i = 0; i < l; i++) {
o[arr[i]] = arr[i];
}

for (i in o) {
r.push(o[i]);
}

return r;
};

예제

1
2
3
4
5
6
var testArray = [1, 2, 1, 3];

testArray = testArray.unique();
testArray = uniqueArray(testArray);

console.log(testArray); // [ 1, 2, 3 ];

여담

언더스코어를 알게된 후로는 lodashuniquniqBy를 사용한다.

API 통신시 Unix Timestamp가 필요한 경우가 있다.

소스

1
2
3
4
// === PHP time();
var timestamp = Math.round(new Date().getTime() / 1000);
// 또는
// new Date().getTime() 을 Date.now() 로 바꿀 수 있다.

여담

lodash 라이브러리를 사용하면 _.now() 함수로 현재 타임스탬프를 가져올 수 있다.
소스처럼 1,000으로 나눠줘야한다.

소스

1
2
3
4
5
6
7
8
9
// function
function comma(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

// prototype
Number.prototype.format = function () {
return this.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};

설명

첫번째는 함수 호출방식이고, 두번째는 NumberValue.format(); 으로 호출하면 된다.

가끔 견적서에 한글 숫자를 써야할 때가 있다.

소스

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* [num2han 숫자를 한글로 변환]
* @param {[integer]} num [숫자]
* @return {[string]} [한글 숫자]
* @author http://www.phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=14981
*/
var num2han = function (num) {
var i,
j = 0,
k = 0;
var han1 = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
var han2 = ['', '만', '억', '조', '경', '해', '시', '양', '구', '간'];
var han3 = ['', '십', '백', '천'];
var result = '';
var hangul = String(num);
var pm = ''; // 부호
var str = [],
str2 = '';
var strTmp = [];

if (Number(num) === 0) {
return '영';
}

if (hangul.substring(0, 1) === '-') {
pm = '마이너스 ';
hangul = hangul.substring(1, hangul.length);
}

if (hangul.length > han2.length * 4) {
return 'too much number';
}

for (i = hangul.length; i > 0; i = i - 4) {
str[j] = hangul.substring(i - 4, i);

for (k = str[j].length; k > 0; k--) {
strTmp[k] = str[j].substring(k - 1, k) ? str[j].substring(k - 1, k) : '';
strTmp[k] = han1[parseInt(strTmp[k])];

if (strTmp[k]) {
strTmp[k] += han3[str[j].length - k];
}

str2 = strTmp[k] + str2;
}

str[j] = str2;

if (str[j]) {
result = str[j] + han2[j] + result;
}

// 4자리마다 한칸씩 띄고 보여준다.
//result = (str[j])? " "+str[j]+han2[j]+result : " " + result;
j++;
str2 = '';
}

return pm + result;
};

설명

숫자를 파라미터로 보내 사용하면 변환된 한글 숫자가 리턴된다.
출처는 소스상에 남겼다. 해당 스크립트를 문법에 맞게 조금 변경했다.

기본적으로 사용하는 기능 외에 custom method 를 추가해서 validation 을 해보자.

목차

  1. 사업자등록번호
  2. 법인등록번호
  3. 바이트 제한
  4. 아이디 체크 (alphanumeric, 숫자 첫글자 불가능)
  5. 비밀번호 체크 (alpah && (number || special char))
  6. datetime (YYYY-MM-DD HH:mm:ss)
  7. date (YYYY-MM-DD)
  8. Kakaotalk Yellow ID
  9. alphanumeric (hyphen, underscore, space 포함)
  10. phone (hyphen 포함)
  11. mobile (hyphen 포함)

소스

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
(function ($) {
$.validator.addMethod(
'biznum',
function (bizID, element) {
var checkID = [1, 3, 7, 1, 3, 7, 1, 3, 5, 1];
var tmpBizID,
i,
chkSum = 0,
c2,
remander;
bizID = bizID.replace(/-/gi, '');

for (i = 0; i <= 7; i++) {
chkSum += checkID[i] * bizID.charAt(i);
}
c2 = '0' + checkID[8] * bizID.charAt(8);
c2 = c2.substring(c2.length - 2, c2.length);
chkSum += Math.floor(c2.charAt(0)) + Math.floor(c2.charAt(1));
remander = (10 - (chkSum % 10)) % 10;
return this.optional(element) || Math.floor(bizID.charAt(9)) === remander;
},
'사업자등록번호 형식에 맞지 않습니다'
);

$.validator.addMethod(
'corpnum',
function (corpID, element) {
var result = true;
if (corpID.length === 13) {
var arr_regno = corpID.split('');
var arr_wt = [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2];
var iSum_regno = 0;
var iCheck_digit = 0;

for (i = 0; i < 12; i++) {
iSum_regno += Number(arr_regno[i]) * Number(arr_wt[i]);
}

iCheck_digit = 10 - (iSum_regno % 10);
iCheck_digit = iCheck_digit % 10;

if (iCheck_digit !== arr_regno[12]) {
result = false;
}
} else {
result = false;
}

return this.optional(element) || result;
},
'법인등록번호 형식에 맞지 않습니다'
);

$.validator.addMethod(
'byte',
function (str, element, param) {
var byte = 0;
var result = true;

for (var i = 0, len = text.length; i < len; i++) {
if (escape(text.charAt(i)).length > 4) {
byte = byte + 2;
} else {
byte = byte + 1;
}
}

if (byte > param) {
result = false;
}

return this.optional(element) || result;
},
'최대 Byte 값을 넘었습니다'
);

// id 체크 (alphanumeric, _- 가능, 숫자가 처음에 올수 없음)
$.validator.addMethod(
'user',
function (id, element) {
return (
this.optional(element) ||
/^([a-zA-Z])[a-zA-Z_-]*[\w_-]*[\S]$|^([a-zA-Z])[0-9_-]*[\S]$|^[a-zA-Z]*[\S]$/.test(
id
)
);
},
'올바른 아이디 형식이 아닙니다'
);

// pw 영문 && (숫자 || 특수문자)
$.validator.addMethod(
'pass',
function (pass, element) {
return (
this.optional(element) ||
/^(?=.*[a-zA-Z])((?=.*\d)|(?=.*\W))./.test(pass)
);
},
'올바른 비밀번호 형식이 아닙니다'
);

// datetime 형식
$.validator.addMethod(
'datetime',
function (datetime, element) {
return (
this.optional(element) ||
/^\d{4}-(0[1-9]|1[0-2])-([0-2]\d|3[01]) (0\d|1\d|2[0-3]):[0-5]\d:[0-5]\d$/.test(
datetime
)
);
},
'올바른 날짜, 시간형식이 아닙니다'
);

// date 형식
$.validator.addMethod(
'date',
function (dt, element) {
return (
this.optional(element) ||
/^\d{4}-(0[1-9]|1[0-2])-([0-2]\d|3[01])$/.test(dt)
);
},
'올바른 날짜 형식이 아닙니다'
);

// 옐로아이디 형식
$.validator.addMethod(
'yellowid',
function (yid, element) {
return this.optional(element) || /^@[\W|\w]{2,15}/.test(yid);
},
'올바른 옐로아이디가 아닙니다'
);

// alpahnumeric _ - space
$.validator.addMethod(
'alphanumeric',
function (v, element) {
return this.optional(element) || /^[a-zA-Z\d\-_\s]+$/.test(v);
},
'올바른 형식이 아닙니다'
);

// 하이픈을 포함한 전화번호
$.validator.addMethod(
'phone',
function (p, element) {
return this.optional(element) || /^\d{2,3}-\d{3,4}-\d{4}$/.test(p);
},
'올바른 전화번호 형식이 아닙니다'
);

// 하이픈을 포함한 휴대폰 번호
$.validator.addMethod(
'mobile',
function (m, element) {
return (
this.optional(element) ||
/^01([0|1|6|7|8|9]?)-(\d{3,4})-(\d{4})$/.test(m)
);
},
'올바른 휴대폰 번호 형식이 아닙니다'
);
})(jQuery);

설명

biznum, byte…와 같은 속성을 추가해 사용하면 된다.
필요한 부분만 복사해 가져가도 되고.

예제

1
2
3
4
5
6
7
8
9
10
$('form').validate({
rules:{
text_field: {byte:80},
date_field: {date:true}
},
messages:{
text_field: {byte:'80자 초과'}
date_field: {date:'날짜 형식 아님'}
}
});

여담

byte check 의 함수 로직이 많지만, 한글 및 특수문자를 2byte 로 정확히 체크해주는 것은 위의 함수 뿐이였다.

jQuery Validation with Materialize CSS
Materialize CSS 와 연동해 사용할 수 있다.

소스

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
// p 태그를 이용하는 방법
$.validator.setDefaults({
errorClass: 'invalid form-error red-text',
errorElement: 'p',
errorPlacement: function (error, element) {
var e = element.get(0);
if (e.type === 'radio' || e.type === 'checkbox') {
var $a = error.appendTo(element.parent());
$a.css({ 'margin-top': '10px' });
} else {
error.appendTo(element.parent());
}
},
});

// 더 예쁜 방법
$.validator.setDefaults({
errorClass: 'invalid',
validClass: 'valid',
errorPlacement: function (error, element) {
var $label = $(element)
.closest('form')
.find("label[for='" + element.attr('id') + "']");

$label.attr('data-error', error.text());
$label.addClass('active');
},
});

여담

bootstrap tooltip 을 활용한 validation 처럼 toast 를 활용한 플러그인이 나오면 좋으련만…

기본 기능만으론 checkbox 나 radio 사용시에 첫번째 element 뒤에 글자가 삽입이 되서 위치를 지정해주어야한다.

소스

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
<script>
$.validator.setDefaults({
onfocusout: false,
invalidHandler: function (form, validator) {
// 커스텀 포커스 핸들링
if (validator.numberOfInvalids()) {
validator.errorList[0].element.focus();
//alert(validator.errorList[0].message); // 경고창
}
},
errorClass: 'text-danger', // 에러 스타일을 입힐 클래스 지정
errorPlacement: function (error, element) {
// data-error 속성으로 해당 위치 삽입
var placement = $(element).data('error');
if (placement) {
$(placement).append(error);
} else {
// 없을경우 마지막노드 뒤에 삽입
element.parent().children().last().after(error);
}
},
});
</script>

<!-- data-error 속성 사용 예시 -->
<input type="text" name="id" data-error="#id_error" />
<p id="id_error"></p>

설명

data-error attribute 를 통해 원하는 위치에 에러를 띄울 수 있고,
그렇지 않을 경우 마지막 노드 뒤에 에러를 출력한다.

7 줄에 주석을 지우면 첫번째 오류를 alert 으로 띄울 수 있다.