Wednesday, January 15, 2014

NUS ModCrasher: Behind the Scene #2 - jQuery/Twitter Bootstrap Copycat on scrolling result



This is a long overdue post,but I will write it anyway. So I wanted to have the collapsible item in ModCrasher. Yos and I were using Bootstrap - thus jQuery - but I wanted a finer control of the scroll and I didn't use any other part of Bootstrap nor jQuery initially (Yos later used it to show the map on the result page). So I was thinking, "Why don't I try making it myself? Must be fun!!" So yeah, I made the collapsible result myself. I can't remember which code I looked at, but I think it was Bootstrap/jQuery code. I followed the idea, except being more general than that.

Here is the JavaScript code:
function onRowClick() {
  var e = window.event;
  var element = e.target;

  while ((element.tagName != "TR") && (!element.className.match(/(?:^|\s)IVLE-link(?!\S)/)) && (!element.className.match(/(?:^|\s)custom-tweet-button(?!\S)/))) {
    element = element.parentNode;
  }

  if (element.id && e.target.id !== 'map-btn') {
    var collapsible = document.getElementById("hidden-"+element.id);

    if ( collapsible.className.match(/(?:^|\s)hide-info(?!\S)/) ) {
      collapsible.style.height = "0px";
      collapsible.className = collapsible.className.replace( /(?:^|\s)hide-info(?!\S)/g , '' );

      var wantedHeight = collapsible.scrollHeight;
      var increment = 10;
      var height = 0;

      function scrollingUnveil() {
        height += increment;
        collapsible.style.height = height+"px";

        if (collapsible.clientHeight >= wantedHeight) {
          collapsible.style.height = collapsible.scrollHeight;
          clearInterval(id);
        }
      }

      var id = setInterval(scrollingUnveil, 5);
    }
    else {
      var wantedHeight = 0;
      var increment = 10;
      var height = collapsible.clientHeight;

      console.log("again");

      function scrollingVeil() {
        height -= increment;

        collapsible.style.height = height+"px";

        if (collapsible.clientHeight <= increment) {
          collapsible.style.height = "0px";
          collapsible.className += " hide-info";
          clearInterval(id);
        }
      }

      var id = setInterval(scrollingVeil, 5);
    }
  }
}

So what was I doing in that code? So I have a CSS class called hide-info which have the attribute display as none. If a row is clicked and it has no hide-info class, reduce the height of the row gradually (I use a linear function here) then add the hide-info class to hide the content fully (for better rendering). If a row is clicked and it has hide-info class, vice versa. To ensure reducing the height of the row is clean, overflow must be set as hidden.

I use a class to identify if the element clicked needs to be hidden or shown. To ensure only one row is hidden or shown, I need to ensure the parent item has a unique name - with Jinja, I use the module's name and the module's schedule as the name since some module has more than one lecture but the lectures would have unique timing.

The problem was there were some buttons within each row, which if clicked will be registered as a row click thus hiding the result entry. I did not want this to happen, so there is an element validator that if such nested elements are clicked, don't register them as row click. This is kind of similar to jQuery not selector which I just learnt recently.

It was quite fun doing the animation since I learnt a lot about JavaScript. Given my limited knowledge of JavaScript, excuse me if my abstraction level was very low in the code.

Have fun coding!

No comments:

Post a Comment