레이블이 jquery인 게시물을 표시합니다. 모든 게시물 표시
레이블이 jquery인 게시물을 표시합니다. 모든 게시물 표시

jQuery Mobile로는 하지 않겠다

jQuery로 잘 동작하는 웹 사이트가 있는데, 터치 이벤트를 이용하고싶어서 조금씩 변형시키다보니 jQuery Mobile까지 시도해보게 되었다.

여러가지 이상한 현상을 겪으면서, jQuery Mobile은 jQuery만큼은 성공하기 어렵겠다는 생각을 했다.

내가 만들지 않은 html tag들이 여기저기 생긴다. 내가 지정하지 않은 class들이 마구 들어간다. jQuery Mobile CSS에서 지정해놓은 복잡한 스타일때문에 정작 내가 지정한 스타일로는 표시되지 않는다.

jQuery가 성공할 수 있었던 것은 다양한 tool을 제공하되 어떤것도 강제하지 않았기 때문이라고 본다. jQuery Mobile은 제공하는 tool은 거의 없고, 제공하는 style만 있는것 같다. 그 스타일링 시스템을 두고 이 개발자들은 framework이라고 과대평가하고있다. 그 스타일에 맞춰서 웹 사이트를 만들 수 있으면 다행인거고, 그 스타일 대로는 웹 사이트를 만들 수 없다면 시간만 낭비한 것이다.


jQuery Mobile, 짧은 만남이었지만, 이젠 그만 안녕~
다시 만날 일이 없었으면 좋겠네!!





touchSwipe 라는 것을 알게 되었다.

참조: http://labs.rampinteractive.co.uk/touchSwipe/docs/symbols/$.fn.swipe.html

여기에서는 다음과 같은 함수들을 제공한다. 정확히 내가 원하던 jQuery extension이다.

swipe:         function(event, direction, distance, duration, fingerCount, fingerData) {}
swipeLeft:     function(event, direction, distance, duration, fingerCount) {}
swipeRight:    function(event, distance, duration, fingerCount, fingerData) {}
swipeUp:       function(event, distance, duration, fingerCount, fingerData) {}
swipeDown:     function(event, distance, duration, fingerCount, fingerData) {}
swipeStatus:   function(event, phase, direction, distance, duration, fingers, fingerData) {}

pinchIn:       function(event, direction, distance, duration, fingerCount, pinchZoom) {}
pinchOut:      function(event, direction, distance, duration, fingerCount, pinchZoom) {}
pinchStatus:   function(event, phase, direction, distance , duration , fingerCount, pinchZoom, fingerData) {}

tap:           function(event, target) {}
doubleTap:     function(event, target) {}
longTap:       function(event, target) {}
hold:          function(event, target) {}

named anchor사이를 오가되 history에 남기지 않는 방법

named anchor를 몇 군데 두고서 여기저기 오가다 이전 사이트로 되돌아가려고 back버튼을 누르면, 안타깝게도 다른 사이트가 아닌 바로 전에 봤던 named anchor로 되돌아간다. Back버튼을 수차례 눌러야만 비로소 이전의 사이트로 되돌아갈 수 있다.

분명 이것은 원하는 것이 아닐것이다.
해결하는 방법은 window.location.replace를 이용하는 것이다.




1. 여기 여러개의 named anchor가 있다.

  <a id='a1' class='named-anchor' href="#a1"> <img src="gif1.gif"> </a>
  <a id='a2' class='named-anchor' href="#a2"> <img src="gif2.gif"> </a>
  <a id='a3' class='named-anchor' href="#a3"> <img src="gif3.gif"> </a>




2. Javascript를 이용하여 named anchor가 클릭되면 history에 흔적을 남기지 않고 바로 이동시킨다.

  $(document).ready( function() {
    $('.named-anchor').click( function() {
      window.location.replace( $(this).attr('href') );
    });
  });





jquery를 이용해서 스크롤하지 못하게 하는 방법

문서의 어느 부분을 보고있건 상관없이 스크롤을 금지하고 허용하려면 다음과 같이 하면 된다.



For desktop:


1. 스크롤바를 하나 둔다.
<div id="scrollbar"></div>

<style>
  #scrollbar {
    position: fixed; top: 0; right:0; height: 100%;
    overflow-y: scroll;
    display: none;
    opacity:0.5;
  }
</style>




2. <body>가 스크롤이 금지되면 빨갛게 표시되도록 하자. <body>는 처음에는 스크롤이 가능하다. (스크롤 금지 = off 이다).
<style>
  body.on { background-color:red; }
</style>

$(document).ready(function() {
  var container = $('body').addClass('off');
 ...
}




3. <html>이 클릭될 때마다 스크롤을 금지하거나 허용한다.
$(document).ready(function() {

  $('html').on('click', function(){
    if(container.hasClass('off')){
      var bw = $('body').width();
      var sw = $('#scrollbar').width();
      var wh = $(window).height();
      var dh = $(document).height();
      container.toggleClass('on off');
      $('body').css('overflow', 'hidden').width( bw-sw );
      if( wh < dh )
        $('#scrollbar').show();
    }
    else if(container.hasClass('on')){
      container.toggleClass('on off');
      $('#scrollbar').hide();
      $('body').css('overflow', '').css('width', '');
    };
  });
});







For mobile:


간단하다. 터치이벤트를 다 먹어 치운다.

  $('body').bind('touchmove', function(e) {
      e.preventDefault();
  });

  $('body').unbind('touchmove');

jquery의 focusin을 이용해서 객체를 잠깐만 보이게 하는 방법

1. 잠깐만 나타날 HTML 객체가 있다.


<p><input type="text"> <span>이름</span> </p>
<p><input type="text"> <span>주소</span> </p>




2. css 속성은 보이지 않는 것이다.


<style>
span { display: none; }
</style>



3. focus in 되면 잠깐동안만 display:none이 안되도록 한다.


<script>
$( 'p' ).focusin(function() {
$( this ).find('span')
.css( "display", "inline" )
.fadeOut( 1000 );
});
</script>




jquery를 이용해서 객체가 화면안에 보이는지 여부를 판단하는 방법

애니메이션이 많은 페이지는 CPU를 많이 사용하게 되고, 사용자는 불편함을 느끼게 된다.
이 상황을 좀 개선시킬수 있는 방법이 있다면, 화면 밖에 있는 애니메이션을 제거하는 것이다. 그 애니메이션 객체가 화면 안으로 들어왔을때 애니메이션 시키고, 화면 바깥으로 나가면 애니메이션을 중지한다.

여기서는 화면 안으로 들어온 객체의 배경색을 빨갛게 변경시켜 보겠다.





1. 애니메이션 객체가 있다. 클래스는 vobj이다. 여기서는 <div>라고 하자.

  <div id="o2" class="vobj"></div>




2. css 속성을 준다.

  .vobj {
    background-color:blue;
  }
  .vobj.visible {
    background-color:red;
  }



3. jQuery 메서드를 확장한다.

  ( function( $ ) {
    $.fn.visible = function() {
      var $t=$(this), $w=$(window);
      var tt = $t.offset().top, th = $t.height(), tb=tt+th;
      var vt = $w.scrollTop(), vh = $w.height(), vb=vt+vh;
      if((tb<vt)||(vb<tt)) {
        console.log('invislble');
        return [false, false];
      }
      else if(((vt<=tt)&&(vb>=tb))||((vt>=tt)&&(vb<=tb))) {
        console.log('fully visible');
        return [true, true];
      }
      else {
        console.log('partly vislble');
        return [/*fully*/ false, /*partly*/ true];
      }
    }
  }) ( jQuery );




4. vobj들을 위한 변수와 함수를 준비한다.

  var vobjs;

  function vobjControl() {
    vobjs.each(function(i, e) {
    var je = $(e);
    var r = je.visible();
    r[1] ?
      je.addClass("visible") :
        r[0] ? 0 /* no op */ : je.removeClass("visible");
    });
  }




5. ready 상태가 되면 vobjControl()을 한번 실행한다. 이후의 실행을 위해서, 화면의 크기변화를 유발하는 이벤트 핸들러와 스크롤 이벤트 핸들러에 등록시킨다.

  $(document).ready( function(){
  
    vobjs = $(".vobj");

    vobjs.each( function( i, e ) {
      var je = $(e);
      if( je.visible()[1] ) {
        je.addClass("visible"); 
      } 
    });
    
    $(window).on('orientationchange resize scroll', function(){
      vobjControl();
    });

    ...

  });




6. animation을 화면에서 사라지게 하려고 display:none을 이용하면 layout jitter를 일으킨다. 그 결과로 화면 밖으로 나가서 보이지 않게된 객체가 실제로는 여전히 보이고 애니메이션을 실행중일 가능성이 50%이다.
Visibility:hidden을 이용하면 이 layout jitter를 피할 수 있다.


javascript를 이용해서 화면 회전간에 화면의 특정 위치에 머물러있게 하는 방법

데스크톱 브라우저의 화면 너비가 변하거나, 휴대폰 브라우저의 화면이 회전하는 경우, 화면에 나타나는 부분은 내가 보고있던 내용과 무관한 경우가 허다하다.

이런 문제를 해결하려면 화면의 너비가 달라지더라도 직전에 보고있던 부분이 화면안에 나타나도록 처리를 해야 한다.




1. 화면의 가로 스크롤 위치를 저장하는 변수를 둔다.

  var sy;





2. scroll 이벤트가 발생할때마다 그 변수를 업데이트시킨다.

  $(window).scroll( function(){
    sy = $(window).scrollTop() / $(document).height();
  });




3. 화면의 너비가 변화시켜 레이아웃을 유발하는 이벤트가 발생하면, 그 변수를 활용해서 적절한 위치로 스크롤 시킨다.

  $(window).on('orientationchange resize', function(){
    $(window).scrollTop( $(document).height() * sy );
  });



jquery에서 touch event 사용하는 방법

jQuery Mobile은 사용하지 않기로 했다.



jquery에서는 mouse 이벤트와 touch 이벤트를 섞어놓았기 때문에, 상황에 따라서는 원하지 않는 결과가 발생하기도 한다 [A].

아래의 예제는 세 개의 영역에 터치 조작을 하도록 구성했는데, 터치 조작이 하나의 영역에서만 일어나고 다른 영역에는 일어나지 않도록 구성하는 것이 쉽지 않았다.



1. 터치하려는 객체를 둔다.

  <body>
    <div class="터치_가능한_객체"> 터치 가능한 객체 1 </div>
    <div class="터치_가능한_객체"> 터치 가능한 객체 2 </div>
    <div class="터치_가능한_객체"> 터치 가능한 객체 3 </div>
  </body>







2. 터치가능한 객체는 굵은 테두리와 넓은 면적을 준다. 터치되면 빨갛게 표시한다.

  <style>
    .터치_가능한_객체 {
      border: 5px solid red;
      min-height:100px;
      margin:30px;
    }
    .터치됨 {
      background-color:red;
    }
  </style>






3. 터치 이벤트를 편리하게 제공받으려면 jquery와 jquerymobile이 필요하다.

  <script
    src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.js">
  </script>
  <script
    src="https://ajax.googleapis.com/ajax/libs/jquerymobile/1.4.3/jquery.mobile.js">
  </script>








4. jquerymobile을 위한 스크립트는 ready 이벤트에 등록하지 않고 pageinit 이벤트에 등록한다.

  <script>
    $(document).on("pageinit",function(event){
      ...
    });
  </script>






5. 터치 가능한 어떤 하나라도 터치되었음을 표시할 전역 flag를 둔다.

  var 터치됨 = false;






6. 터치 이벤트를 요령껏 관리한다.

한꺼번에 여러군데를 터치했다가 손가락을 떼면 여러개의 touchend 이벤트가 발생한다. 이 중에 하나만이 의미있는 touchend이고 나머지들은 잡음성 이벤트들이다. 이것을 구분해내기위해서 각 터치_가능한_객체에도 "터치됨"이라는 flag를 둔다.

  $(".터치_가능한_객체").on("touchstart", function( event ){
    if( !터치됨 ) {
      터치됨 = this.터치됨 = true;
      $(this).addClass("터치됨");
      event.preventDefault(); /* [A]를 회피하려면 이것이 필요하다 */
      $(this).text('touchstart');
    }
  }); 

  $(".터치_가능한_객체").on("touchend", function( event ){

    if( this.터치됨 ) {
      터치됨 = this.터치됨 = false;
      $(this).removeClass("터치됨");
      $(this).text('touchend');
    }
  }); 



tap, swipeleft, swiperight 이벤트들은 터치가 끝나지 않은 상황에서도 올라온다. 터치된 객체에서 올라오는 이벤트만 의미있는 이벤트이다.

  $(".터치_가능한_객체").on("tap",function(event){
    if( this.터치됨 ) {
      $(this).text("tap");
    }
  });

  $(".터치_가능한_객체").on("swipeleft",function(event){

    if( this.터치됨 ) {
      $(this).text("swipeleft");
    }
  });  

  $(".터치_가능한_객체").on("swiperight",function(event){

    if( this.터치됨 ) {
      $(this).text("swiperight");
    }
  }); 






창의 크기변화에 따른 각종 width와 height의 변화 관찰

창의 너비를 변화시키는 실험을 해보자.



창의 너비가 충분한 경우 각 width의 변화를 살펴보면

    일정:
        없음

    변화:
        window,  html, document

    서로 같아요:
        window,  html, document

    상하 스크롤바가 나타나는 조건:
        window < document




창의 너비가 부족한 경우에는

    일정:
        document

    변화:
        window,  html

    서로 같아요:
        window,  html

    상하 스크롤바가 나타나는 조건:
        window > document







창의 높이를 변화시키는 실험을 해보자.



창의 높이가 충분한 경우 각 height의 변화를 살펴보면,

    일정:
        html,  body

    변화:
        window,  document

    서로 같아요:
        window,  document

    상하 스크롤바가 나타나는 조건:
        window < html




창의 높이가 부족한 경우에는

    일정:
        html,  body

    변화:
        window

    서로 같아요:
        html,  document

    상하 스크롤바가 사라지는 조건:
        window > html




javascript로 한 이미지의 url을 다른 것으로 바꿔치기

휴대폰을 위한 웹페이지를 만드는데, 배경에 사용될 그림의 크기가 1920 x 11400이고, 파일의 크기가 4MB나 된다.
이 비대한 배경이미지때문에 넓은 부분이 하얗게 남아있고, 레이아웃중인 어정쩡한 상태의 화면이 장시간동안 나타나며, 그 시간동안 사용자의 조작에 응답하지 못한다.

해결방법은 배경이미지의 크기를 아주 작게 만들어서, 큰 배경이미지가 주는 부담을 제거하는 것이다.

그래서, 처음에는 50x297 크기의 배경이미지를 사용하고, 나중에 더 높은 해상도의 배경이미지로 교체하는 방법으로 이 문제를 해결했다.

이 방법을 적용하고나니 처음에는 생각하지 않았던 좋은 점이 생겼는데, 화면이 작은 휴대폰에는 너무 큰 이미지를 사용하지 않아도 된다는 것이다. 화면 크기가 480 x 800인 휴대폰에 1920 x 11400 크기의 이미지가 무슨 소용인가? 더군다나 사용자가 zoom도 하지 못하게 차단시킨 상태인데.



바꿔치기하는 방법은 다음과 같다.


1. img 태그를 둔다. 처음에는 width=50px인 이미지를 표시한다. 웹페이지가 열리면서 이 작은 이미지가 재빠르게 로드되고, 이미지는 확대되어 화면 가득 표시된다. 굉장히 저화질의 화면을 얻게 된다.

  <img id="bg" src="poo-50.jpg" />

poo-50.jpg는 너비가 50픽셀이고,
poo-150.jpg는 너비가 150픽셀이고,
poo-1920.jpg는 너비가 1920픽셀이다.




2. 이 이미지의 로딩이 완료되면 load이벤트가 발생하는데, 이때 해상도가 더 높은 이미지로 교체한다. 그런데 이미지가 캐쉬되어있으면 load이벤트가 없다고 한다. 그래서, 이미 로딩이 완료되었는지를 확인하여 로딩완료된 경우라면 load() 이벤트 핸들러를 호출해준다.

  $(document).ready( function(){
    $('#bg').on('load', higher_resolution).
         each( function() {
           if( this.complete ) $(this).load();
         });
    ...
  });




3. higher_resolution() 이라는 함수는 다음과 같다.

  var res = 50;

  function higher_resolution() {

    switch( res ) {

      case   50:
        res=150;
        setTimeout('$("#bg").attr("src", "poo-150.jpg")', 500);
        break;

      case  150:
        res=1920;
        setTimeout('$("#bg").attr("src", "poo-1920.jpg")', 500);
        break;

      case 1920:
        break;
    }
  }




4. poo-50.jpg가 로드되어 화면에 나타난 후 500ms를 기다렸다가 poo-150.jpg를 로딩하기 시작한다. 이 500ms라는 시간동안 브라우저는 레이아웃 등의 작업을 하게 된다. poo-150.jpg가 로드되어 화면에 나타나면 500ms후에 poo-1920.jpg를 로딩하기 시작한다.




5. 아래와 같이 조건문을 하나 삽입하면, 휴대폰 화면 크기보다 큰 이미지를 불필요하게 로딩하는 경우를 피할 수 있다.

  var res = 50;
  function higher_resolution() {
    switch( res ) {
      case   50:
        if($(window).width() > res)
          res=150,
          setTimeout('$("#bg").attr("src", "poo-150.jpg")', 500);
        break;
      case  150:
        if($(window).width() > res)
          res=1920,
          setTimeout('$("#bg").attr("src", "poo-1920.jpg")', 500);
        break;
      case 1920:
        break;
    }
  }





6. 위의 예들은 최저해상도의 이미지에서 조금씩 높은 해상도의 이미지로 점진적으로 바꿔치기하고 있다.
최저해상도 이미지에서 최적의 해상도 이미지로 단 1회 바꿔치기를 하려면 다음과 같이 한다. 뿌옇게 답답한 화면이 좀 오래 지속되는 반면에 트래픽을 더 많이 감소시킬 수 있다.

  function higher_resolution() {
    var ww = $(window).width();
    var rs = [50, 150, 800, 1300, 1920];
    if( ww > res ) {
      for( var i=0; i<rs.length; i++ ) {
        if( ww <= rs[i] ) break;
      }
      if( rs.length == i ) {
        res = 999999999;
        src = "poo-" + rs[ rs.length-1 ] + ".jpg";
      }
      else {
        res = rs[i];
        src = "poo-" + rs[i] + ".jpg";
      }
      setTimeout('$("#bg").attr("src", "' + src + '")', 1000);
    }
  }





7. 화면 폭의 변화는 휴대폰이 회전되었을때도 일어난다. 그래서 다음과 같은 코드도 필요하다.

  $(document).ready( function(){
    ...
    $(window).on('orientationchange resize', function(){
      higher_resolution();
    });
    ...
  )};

화면을 좌우로 문질러서 페이지 이동하기

참조: http://demo.dsheiko.com/spn/


필요한 파일들:


위의 파일들은 웹페이지간을 이동하도록 만들어져있다.
동일한 페이지안의 다른 target들간을 이동하도록 만들어보고싶다.

임의의 내용을 서버에 전송하여 파일로 저장하기


보내는 쪽
<script type="text/javascript">
        function send() {
            $.post(
                'receive_dom.php',
                { data: $("#data").html() },
                function( output ) {
                    $('#output').html( output );
                }
            );
        }
</script>




받는 쪽
<?php

$data = $_POST['data'];

$num = 15365;
$dir = intval($num / 1000);
mkdir( $dir, 0777 );

$file = $num - $dir * 1000;
$filename = $dir . "/" . $file . ".html";

file_put_contents( $filename, $data );
?>

done: click을 이용해서 동적으로 생성된 DOM class에 jquery 함수를 적용시킨다

참조: stackoverflow

ajax로 생성된 new-variant-image에 jQuery의 fileupload() 함수를 실행시키려고 하는데, 다음과 같은 코드는 동작하지를 않는다. 코드 실행 시점에 객체가 존재하지 않기 때문이다.



ajax가 객체를 생성하는 시점에 저 코드를 실행시키면 3초 정도 브라우저가 얼어버린다.

----

제안된 해법은, done: 에 click 이벤트 핸들러를 등록하는 것이다.

ajax에서 success와 done 지정하기

$.ajax({
    type: "POST",
    url: "robots.txt",
    data: "my data",
    success: function(data) {
        if (data != null) {
            alert( "Received something" );
        } else {
            alert ("Received NULL");
        }
    }
})
.done( function() {
    alert("done");
});

JSONP로 cross domain문제 해결하기

지금 소개하는 방법은, 본인이 운영중인 두 개의 도메인간에 데이터를 주고받을 수 있게 해준다.
결코 자기와 상관없는 두 도메인간에 데이터를 주고 받지는 못한다.



그러면 시작해 보자.

해결해야할 문제

http://mydomain_2.co.kr/service.php 의 내용은 다음과 같다.
{"name":"silver", "age":"10"}


http://mydomain_1.co.kr/get_data.html 의 내용이 다음과 같다고 하자.
<script type="text/javascript">
      $.ajax({
        url: "http://mydomain_2.co.kr/service.php"
      })
      .done(function( j ) {
        alert( j.name );
      });
    </script>


http://mydomain_1.co.kr/get_data.html은 silver라고 표시하지 못한다.
그 이유는 jQuery가 인터넷의 평화를 위해서 서로 다른 도메인간에는 데이터를 읽어오지 못하도록 차단하기 때문이다.
mydomain_1.co.kr에서 mydomain_2.co.kr의 데이터를 가져오려고 하기때문에 jQuery가 훼방을 놓고있는 것이다.


해결방법

만약에 내가 mydomain_1.co.kr과 mydomain_2.co.kr을 통제할 수 있는 사람이라면, 위의 두 파일을 살짝 고쳐서 http://mydomain_1.co.kr/get_data.html이 silver라고 표시하도록 만들 수 있다.

http://mydomain_1.co.kr/get_data.html 의 내용을 다음과 같이 수정한다.
<script type="text/javascript">
      $.ajax({
        url: "http://mydomain_2.co.kr/service.php?callback=?"
,dataType : "jsonp" }) .done(function( j ) { alert( j.name ); }); </script>
callback=? 라는 표현은 jQuery가 cross domain 기능을 제공하기위해 사용하는 키워드같은 것이다. 그대로 타이핑해 넣어야 한다.


http://mydomain_2.co.kr/service.php 의 내용을 다음과 같이 수정한다.
<?php
echo $_GET['callback'] . '({"name":"silver", "age":"10"})';
?>
JSON 데이터를 padding하고있는 이 모습을 가리켜 JSON with padding이라 말하고, JSONP라고 줄여서 말한다.
$_GET['callback']을 출력해보면 길이가 아주 긴 임의의 문자열인 것을 확인할 수 있다. 그 문자열은 jQuery가 즉흥적으로 만들어낸 함수이다. (아마도 json parser 함수일 것이다).


아무튼, 이제 http://mydomain_1.co.kr/get_data.html이 silver라고 표시하게 되었다.

-------------

service.php 파일을 다음과 같이 수정하면 "이것이 JSONP!!" 라고 화면에 출력된다.

<?php
echo "alert( '이것이 JSONP!!' )";
?>

파일을 더 간단하게 만든다면,
alert( '이것이 JSONP!!' )



JSONP가 어떻게 돌아가고 있는지 느낌이 오는가?

callback=? 만 넣어주면 jQuery는 상대방 서버가 보내온 데이터를 단순한 데이터가 아닌 실행해야 할 함수로 취급한다.
jQuery가 지어준 함수의 이름을 그대로 사용하면 json parser가 실행되고, 그 외의 javascript 함수이름을 사용하면 그 함수가 실행된다.