HTML and Javascript tips by cella | cella | 2013 | Branchable | 0 | 11879 |
(1) (12) SVG element G 에 path 를 두 개 넣었는데, g.children[] 로 path 를 refer 할 수 없었다. 대신에 g.childNodes[] 로 할 수 있었다. (13) SVG path 에서 끝에 arrow head 를 넣고 싶었다. http://stackoverflow.com/questions/7530689/how-do-you-draw-an-arrow-at-the-end-of-a-bezier-curve-in-svg-raphael 에 따르면 marker 를 쓰면 된다고 하는데, 이 방법에는 문제가 있다. 먼저 arrow head 가 좌우만 가리키고 path 의 각도에 따라 변하지 않는다. 더 심각한 문제는 head 가 path 끝에 더해진다는 것. path 에 겹쳐져야 하는데. 그래서 control point 와 end point 를 이용해서 적당한 각도로 돌아간 정삼각형을 path 로 그렸다. 다음과 같다. function makeArrowHeadD( fromX, fromY, toX, toY) { var control2x = 0.5 * fromX + 0.5 * toX; var control2y = 0.1 * fromY + 0.9 * toY; var xp = control2x - toX; // parallel translation var yp = control2y - toY; var theta = Math.atan( yp / xp); if( xp < 0) theta += Math.PI; var x = 5 * Math.cos( theta); // of (5 * unit vector) var y = 5 * Math.sin( theta); // points of arrow-head-triangle var x1 = x * 0.866 - y * 0.5; // 0.866 = cos(30 deg.), 0.5 = sin(30 deg.) var y1 = x * 0.5 + y * 0.866; var x2 = x * 0.866 + y * 0.5; var y2 = x * (-0.5) + y * 0.866; x1 += toX; y1 += toY; x2 += toX; y2 += toY; return "M " + toX + " " + toY + " L " + x1 + " " + y1 + " L " + x2 + " " + y2 + " Z"; } (14) div 의 style.display 를 none 으로 초기화했다. 그리고 다른 button 을 click 할 때마다 inline 으로 toggle 되게 했다. (event listener 를 통해서) 그런데 다음과 같이 className 으로 초기화를 하면 마치 event listen 를 add 할 시점에는 초기값이 none 이 아니라 undefined(정확한 값을 확인해 보지는 않았음) 값이 들어간 것처럼 행동한다. 하지만 실제로 그 div 를 load 할 때는 none 값이 들어가 있는 것처럼 화면에 display 되지 않는다. 참고로, addEventListener 는 className 에 assign 하는 것 다음에 나온다. 한 function 안에 있다. 그런데, 다음에서 comment 를 풀면 none 으로 초기화가 잘 되는 것 같다. 아무튼 undefined 가 들어있을 가능성을 고려해서 toggle 함수를 수정했다. 아직 이유는 모르겠다. var boxmenu = document.createElement("div"); boxmenu.className = "boxmenuStyle"; //boxmenu.style.display = "none"; (15) 어떤 element 에 addEventListener 로 touchend 에 연결시켜 놓은 event listener 에서 event.preventDefault() 를 해 놓았더니, 그 element 에 대해서 mousedown 등등의 다른 event 들이 fire 되지 않는다. (iOS simulator 에서 mouse 로 click 했다. 그리고 touchstart, touchmove 에서는 원래 preventDefault() 가 없었다.) event.preventDefault() 를 없앴더니 mouse event 들, click event 는 fire 된다. (click 은 원래 mousedown + mouseup 이라고 함.) 그런데 focus 는 여전히 fire 되지 않는다. 그래서 하나의 독립적인 div 를 document.createElement 로 만들고 addEventListener 를 사용했는데, focus 만 역시 fire 되지 않았다. 마치 iOS 에서는 아예 지원이 되지 않는 것 같다. 하지만 애플의 자료에 의하면 지원한다고 돼 있었다. (참고로 drag, drop 같은 것은 지원하지 않는다고 나와 있다.) https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html 조금 더 찾아보니까, html 의 모든 element 가 focusable 한 게 아니라고 한다. focusable 의 조건은 다음과 같다. http://www.w3.org/html/wg/drafts/html/master/single-page.html#focusable (16) UIWebView 에서 div 를 contenteditable 로 세팅하고 그 안에 http://.../ 를 넣으니까 자동으로 detect 를 해서 link 로 인식하고 link 표시로 바뀐다. 하지만 edit 하자마자 바뀌는 것은 아니고 다시 load 를 하면서 인식하고 바뀐다. 어떤 패턴의 string 을 인식해서 바꿀지는 webview.dataDetectorTypes 가 결정하는데 아마 default 로 모든 type 들이 켜져 있는 것 같다. (그런데 NSTimer 를 이용해서 확인해 봤는데, [webview reload] 해서는 안 되고 [webview loadHTMLString ...] 을 다시 해야지 다시 load 된다. 왜 그런지 모르겠다.) UIWebView 에서 http link 를 click 했을 때 동일한 바로 그 UIWebView 에서 새 page 가 loading 된다. 그러면 go back button 이 따로 있지 않아서 곤란하다. iPhone 의 messge app 같은 경우에는 link 를 click 했을 때 safari 가 뜨면서 이 safari 에서 새 page 가 load 된다. 이렇게 하려면 .h 에서 즉, [[UIApplication sharedApplication] openURL:[inRequest URL]]; 에서 safari 를 띄우는 듯. ... self.webview.delegate = self; ... - (BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType { if ( inType == UIWebViewNavigationTypeLinkClicked ) { [[UIApplication sharedApplication] openURL:[inRequest URL]]; return NO; } return YES; } (17) UIWebView 에서 UIWebView 에서 camera roll 을 선택하는 view 를 띄우고 image 를 선택하고 그 image 를 받아오는 것 또는 그것의 asset URL 을 알아내는 것까지는 가능하다. http://stackoverflow.com/questions/2915881/access-camera-from-uiwebview 그런데 이 image 를 다시 UIWebView 에서 보여주는 방법이 쉽지 않다. image 는 아니지만 video 파일에 대해서 잘 된다는 주장이 있다. http://stackoverflow.com/questions/18707207/ios-assets-url-confusion asset URL 은 Asset Library 에서만 인식되고 UIWebView 는 못 한다는 주장도 있다. http://stackoverflow.com/questions/12875075/using-alasset-photo-in-uiwebview-img-tag 안 된다는 것은 작년 글이고 된다는 것은 올해 글이라서, 그 사이에 변화가 있었을 수도 있다. 그래서 된다는 사람의 방식을 직접 iOS simulator 에서 구현해 봤는데 잘 안 된다. 이미지 파일은 그냥 supporting files 디렉토리에 Canada.png 를 넣어서 해 봤다. embed 대신에 img 를 사용해도 안 된다. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { [self dismissViewControllerAnimated:YES completion:nil]; NSURL* url = [info objectForKey:UIImagePickerControllerReferenceURL]; ... 에서 url 을 받아오는 것까지는 잘 되는 것 같다. [url absoluteString] 을 찍어보면 "assets-library://..." 가 찍힌다. UIImage* image = [info objectForKey:UIImagePickerControllerOriginalImage]; NSData *imageData = UIImageJPEGRepresentation (image, 0.1); [self.webview loadData:imageData MIMEType:@"image/jpeg" textEncodingName:@"UTF-8" baseURL:nil]; 로 이미지를 직접 보여주는 것도 가능하다. ps. video file 을 photo library 에 넣어서 확인해 보니까, video 는 잘 된다. URL 을 UIImagePickerController 로 부터 다음과 같이 받아오면 된다. (그런데 iOS simulator 에서 iPhone 으로 설정하면 audio 부분에서 error 가 난다. 그냥 Photos 앱에서 play 하는 것도 같은 에러가 난다. iPad 에서 하면 잘 된다.) NSURL* url = [info objectForKey:UIImagePickerControllerMediaURL]; 이 경우 [url absoluteString] 은 file://... 의 형태를 갖는다. assets-library 가 아니라. image file 의 경우에는 이렇게 url 을 받아오면, url == nil 이다. UIImagePickerControllerDelegate Protocol Reference 에 다음과 같은 구절이 있다. ... imagePickerController:didFinishPickingMediaWithInfo: ... info A dictionary containing the original image and the edited image, if an image was picked; or a filesystem URL for the movie, if a movie was picked. pps. 이 작업을 완성하려면, objective C 부분에서 선택한 파일에 대한 정보를 html 안에 끼워넣어야 한다. movie 파일의 경우 다음과 같이 할 수 있다. NSString* javascriptFunctionCall = [NSString stringWithFormat:@"addEmbed(\"%@\",\"%@\")", self.fileToUpload.elementId, self.fileToUpload.urlString]; [self.webview stringByEvaluatingJavaScriptFromString: javascriptFunctionCall]; addEmbed() 는 javascript function 이다. http://stackoverflow.com/questions/12384498/returning-values-to-javascript-when-calling-an-objective-c-function (18) var img = document.createElement("img"); img.width = "80px"; img.height = "80px"; 로 하면 안 된다. mg.width = "80"; img.height = "80"; 로 하면 된다. 찾아보니까, px 를 붙이는 것은 CSS style 에서 하는 것이고 attribute 에는 그냥 수를 쓴다는 얘기가 있다. 즉, img.style.width = "80px"; 같은 식으로 하면 된다는 것. 그런데, embed.width = "80px"; 같은 식으로 해도 잘 됐었는데. 이상하다. (19) JSON.parse() 는 object 를 반환하지만 generic object 다. properties 는 잘 들어가 있지만, 그 object 가 특정 object 라는 것을 나타내지는 못한다. 따라서 특정 object 로 변환해 주는 과정이 필요하다. 예를 들어 특정 object 의 constructor 에서 duplicate 할 수 있도록 정의하고 그 constructor 를 사용해서 새 object 를 만들면 된다. (20) button 에 addEventListener("touchend", listener, false) 를 하고 listener 내에서 var empty = confirm("really?"); 를 이용해서 사용자의 확인을 받고 진행하는 방식인데, alert() 를 사용할 때처럼 button 이 두 번 click 되는 부작용이 생겼다. (바로 다음에 다른 element를 click 해도 이 button 이 click 된 것처럼 반응한다.) 그런데 "touchend" 를 "touchstart" 로 고쳤더니 그러지 않는다. 다른 것들이 대부분 touchend 로 돼 있어서 그러는지... 아니면 다른 이유인지, 이유는 모르겠다. 결국에는, touchstart 를 쓰기 싫어서, setTimeout() 을 이용했다. setTimeout() 는 그 속에 alert() 나 confirm() 을 포함한 callback 함수를 event queue 의 맨 마지막에 집어넣는다. 그러면 설령 setTimeout( function(){...}, 0} 으로 하더라도, (즉, 0 만큼 delay 되더라도 queue 의 마지막이므로) 현재 진행중인 event 들 사이에 끼여서 뭔가 예측하지 못한 일이 발생하는 것을 막는 것 같다. event queue 의 내용을 알아내거나 clear 하는 방법을 찾아보려고 했는데 잘 못 찾겠다. (21) box object 는 outbox 라는 property 를 가지고 있는데 이것은 이다. 이 div 를 document.body.appendChild() 하기 전에 outbox.offsetHeight 나 offsetWidth 의 값은 0이다. 즉, 이 값들은 document body 에 들어가고 나서 적당한 값이 들어가게 된다. |