Perfect paginator

    I want to talk about the evolution of the “paginator” - such a component for navigating through the pages of a news feed (or a list of selected events or anything else) and sharing one trick that allows you to put fewer handlers for homogeneous components or not to put a handler for new items loaded each time through an Ajax request.

    The paginator component is a set of links with page numbers that immerse the result of some ajax request into a div. At the same time, the ajax-request returns not only the content, but also the links themselves to go to other pages, so for them, handlers were set again for each load, for this, the corresponding script tag came along with the links.

    When I came to the project, it looked something like this:


    {%num%}
    СюдаТуда

    Now I regret that I did not keep this masterpiece exactly ... but for the first time, my hair just stood on end from such a thing. And this was not once on the main, but in several sections and it was propagated by copy-paste.

    The first thought on how to overcome this mess was to combine handlers and get rid of class selectors. The second is to make universal code that could be in a separate js-file and apply on any page of the site through a single line call. The first was solved without problems, the second was clearly not worth it, because it turned out seven lines of code, which it’s a shame to put it into a separate file. But even if they could be taken out, the problem of re-executing the script at each boot would not solve it:






    In addition to the lack of copy paste and re-execution at each page load, this solution also has the disadvantage that it is not data-driven, that is, both the path and the target element are hardcoded in the code. It is clear that here is a page, and the data from the code is separated by several lines, but I still wanted to do it “wisely” so that the data from the code was stored separately and there was no need to put handlers on every page load. As a result, we got such a small classic - its instance is created when the page loads, just in $ (document) .ready And the links themselves now look like this: And after loading them from the newly arrived ajax file, you do not need to put any handlers.

    ;function CPaginator( $, settings )
    {
      ///////////////////////////////////////////////////////////////////////////////////////////  
      function onPageSelect( e )
      {
        var dash = this.href.indexOf('#');
        var dashslash = this.href.indexOf('#/');
        var path;
        if ( -1 != dashslash ) path = this.href.substring(dashslash+1);    
        if ( -1 != dash && -1 == dashslash ) path = window.location.pathname;  
            
        if ( path && this.name != "page=" )   
        {
          var rel = this.rel;
          var target = $(rel||settings.defaultTarget).html("loading...");
          $.post( path, this.name, function( msg ) { target.html( msg ); });    
        }
        if ( e.preventDefault )
          e.preventDefault();
        return false;
      }
      ///////////////////////////////////////////////////////////////////////////////////////////    
      function catchPaginatorClick( ev )
      {
        if ( !ev ) ev = window.event;
        var target = ev.srcElement || ev.target;
        return ( target && target.name && -1 != target.name.indexOf('page=') ) ? onPageSelect.call( target, ev ) : true;
      }
      ///////////////////////////////////////////////////////////////////////////////////////////  
      function constructor()
      {
        document.onclick = catchPaginatorClick;
      }
      ///////////////////////////////////////////////////////////////////////////////////////////
      constructor();
    }



    {%num%}


    In the link itself in href you can write after the lattice the path where the post-request for data will be carried out, in rel (although this contradicts the original purpose of this attribute), the selector of the element where the result will be written is indicated, and the arguments for the request are stored in name.

    The handler for the click event is placed on the entire document, and now it does not matter how many elements are on the page, how and when they were loaded - they do not need to put handlers for everyone personally. The handler itself should determine as quickly as possible whether there was a click on the link for which a special logic of work was prepared, or if the event belongs to another element for which it is necessary to enable the default browser logic.

    Thanks to onPageSelect.call, the source element of the event in this and the event itself in the first argument are passed to the function. After the source properties are parsed, the ajax-post-request itself goes and it returns false from the handler and preventDefault is executed in those browsers where this method is defined. Without these lines, the browser will try to follow the link, and even if it looks like # / dir / 7 /, IE will hear a characteristic click that occurs when a new page is opened.

    In general, according to TRIZ, an ideal object is such that it does not exist, but its function is fulfilled. Here, it happened like this, that there are no handlers hung directly on constantly overloaded elements, but their function is performed. Here you can ask, why make the server work logic such that page transition controls come back from there every time? I can only answer this by the fact that managers are driven by deadlines, bugs arrive decently and time servers are not refactored, so I have to adjust.

    If anything, those long comments between class methods will not fall into a productive environment and will be cut out by a YUI

    PS compressor : first post on a habr
    PPS: all sorts of wishes and suggestions on style and content are welcome, because I still have something to tell (and I want to do it in the best way)

    Also popular now: