Implementing basic Slim scroll

In this article, I am going to implement a basic slim scroll which is almost achieved by using just css. First of all, we need to hide the system’s ugly scroll (I am talking about Windows). To hide the scroll, we need to wrap the content container with another element. This wrapped element will have overflow: hidden and the content container element will have overflow: auto and some padding at right to hide the scroll. Now just we need to apply events like scroll, mouseup, mousedown and mousemove.

In the below example, I have used prototypejs to attach events. If you are new to prototype and you good with javascript then there is no need to worry. you can just refer the below prototypejs code which is easy to understand.

I have implemented only the scroll event using prototypejs. I hope you can implement the other events by referring the below code. Enjoy!!

HTML

<div class="content-wrapper">
    <div class="content-holder">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
        <div class="scroll-bar"></div>
    </div>
</div>

CSS

.content-wrapper {
    width: 380px;
    overflow: hidden;
    display:inline-block;
    position:relative;
}
.content-holder {
    overflow: auto;
    padding-right:18px;
    line-height: 15px;
    padding-bottom: 2px;
    width: 100%;
    height: 90px;
}
.scroll-bar {
    background-color: #999999;
    border-radius: 10px;
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    position: absolute;
    right: 0px;
    top: 0px;
    width: 5px;
    opacity: 0.5;
}

Javascript (prototypejs)

var SlimScroll = Class.create();
SlimScroll.prototype = {
    initialize: function () {
        this.scrollKit();
    },
    scrollKit: function () {
        var scrollElement = $$('.content-holder')[0];
        var height = scrollElement.offsetHeight;
        var scrollHeight = scrollElement.scrollHeight;
        var scrollPercentage = (height / scrollHeight);
        var scrollBarElement = $$('.scroll-bar')[0];
        if (scrollHeight > height + 5) {
            scrollBarElement.setStyle({
                height: (height * scrollPercentage) + "px"
            });
            Element.observe(scrollElement, 'scroll', this.scrollBar);
        } else {
            Element.hide(scrollBarElement);
        }
    },
    scrollBar: function (e) {
        var elem = e.currentTarget;
        var height = elem.offsetHeight;
        var scrollHeight = elem.scrollHeight;
        var scrollTop = elem.scrollTop;
        var percentage = (height / scrollHeight);
        var barPosition = scrollTop * percentage;
        var scrollBar = $$('.scroll-bar')[0];
        scrollBar.setStyle({
            top: barPosition + "px"
        });
    }
}

var slimScroll = new SlimScroll();

JSFIDDLE

Having same gap between different width elements

I created code for keeping a fixed gap among the elements even when the width of each element is different. There seems not much to be explained. please comment below if you can’t understand anything.

HTML

<div class="row" id="row1">
    <div class="box" id="box1"></div>
    <div class="box" id="box2"></div>
    <div class="box" id="box3"></div>
</div>

CSS

div {
    font-size: 0;
}
#row1>div {
    position: absolute;
    height: 100px;
}
#box1 {
    width: 50px;
    background-color: red;
}
#box2 {
    width: 90px;
    background-color: blue;
}
#box3 {
    width: 50px;
    background-color: green;
}

 #row1 {
    width: 500px;
    background-color: #DCDCDC;
    height: 100px;
    position: relative;
}

Javascript

function setAlign(parentClass, childCommonClass) {
        var childDivs = document.getElementsByClassName(childCommonClass);
        var childDivsTotalWidth = 0;
        var childDivsLength = childDivs.length;
        var parentElement = document.getElementsByClassName(parentClass)[0];
        var parentElementWidth = parentElement.offsetWidth;
        for (var i = 0; i < childDivsLength; i++) {
            childDivsTotalWidth += childDivs[i].offsetWidth;
        }
        var remainingWidth = parentElementWidth - childDivsTotalWidth;

        var gap = remainingWidth / (childDivsLength + 1);
        var leftWidth = gap;
        for (var j = 0; j < childDivsLength; j++) {
            if (j > 0) {
                leftWidth += gap + childDivs[j - 1].offsetWidth;
            }
            childDivs[j].style.left = leftWidth + "px";
        }
    }

    window.onload = setAlign('row', 'box');
    window.onresize = function () {
        setAlign('row', 'box');
    }

Working fiddle

Ellipsis on second line using pure css

Guys while working to get ellipsis on second line in my project, I realized we can do this using just CSS!!

PROS:
The solution supports IE9+ and ofcourse all modern browsers.

CONS:
- Looks misplaced for long words.

<div class="ellipsis">
    <span>...</span>
    Hello this is Mr_Green from Stackoverflow. I love CSS. I live in CSS and I will never leave working on CSS even my work is on other technologies.
</div>
div {
    height: 3em;
    line-height: 1.5em;
    width: 80%;
    border: 1px solid green;
    overflow: hidden;
    position: relative;
}
div:after {
    content:". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  . . . . . . . . . . . . . . . . . . . . . . . . . . . .";
    background-color: white;
    color: white;
    display: inline;
    position: relative;
    box-shadow: 8px 1px 1px white;
    z-index: 1;
}
span {
    position: absolute;
    bottom: 0px;
    right: 0px;
    background-color: white;
}

Explanation:

Below are the points to remember:

  • If you want the ellipsis on second line then make sure that the height you have given is the multiple of line-height and number 2(second line, the number will be 3 to get the ellipsis on third line.)
  • Look how I z-indexed the span’s three dots and the after pseudo element’s dots. which plays the main role in getting this ellipsis like structure.
  • Make sure the dots in content property fits the container if there is no text.
  • Make sure the background-color and color css properties of the pseudo element of content has the same color as the background color of the content element.
  • The space between each dot (.) makes sure that the ellipsis will be seen when the content is overflown out of the container or if the content is very near to get overflown (drawback).
  • Also make sure the box-shadow color of the pseudo element is similar to the background color of the content element.

NOTE: If you use word-break: break-all; on the content element then there will be no drawbacks except the words will break. if it is ok for your project then this solution is perfect.

I hope you understood my explanation. If not then please feel free to leave a comment below. Happy coding :)

Ajax loading of product collection in product listing page – Magento

Today I got in to a situation where I need to do ajax loading of project collection in product listing page. if we search for the same issue on google then we will get many extensions which are free but they include plugins like infinitescroll.js, jquery.js, jquery-ui.js etc.. instead of this you can use prototype.js which is available by default in magento. Actually, this task is simple if we understand it.

By default, Magento shows product listing in page wise format. Each page numbering is a anchor tag with href which loads the complete page with new project listing related to the clicked page number.

In this example, I am going to create a simple product loading which happens whenever I click on a button. This button will be visible at the bottom of the product listing section, if the product collection is more than one page. Place this button inside {$theme}/template/catalog/product/list.phtml.This button actually triggers the “Next” functionality link which you can see in the below image:

Capture

So, whenever we click on the button this actually gets the next page product collection. If we inspect the “Next” link element (which is shown in the above image), we can see that it has two class names: next and i-next. So, to trigger click event on this element with the button we can use javascript.

<button id="load-more-products">Load more products..<button>
function sendLoadMoreProductsRequest(url) {
	new Ajax.Request(url, {
	  	onSuccess: function(response) {
	  		//Create dummy element
	  		var div = document.createElement('div');
	  		div.innerHTML = response.responseText;
	  		//refresh the page class element
	  		$$('.pages')[0].innerHTML = $(div).select('.pages')[0].innerHTML;
	  		//append the list to the existing product list
	  		$$('.category-products')[0].innerHTML += $(div).select('.category-products')[0].innerHTML;
	  		//check if there are more products to be loaded or not
	  		if(!$(div).select('.next.i-next')[0]){
	  			$('load-more-products').style.display = "none";
			}
	  	}
	});	
}

function callbackFunc(e) {
	if($$('.next.i-next')[0]){
		var nextPageUrl = $$('.next.i-next')[0].readAttribute('href');
		sendLoadMoreProductsRequest(nextPageUrl);
	}
	else{
		//hide button
		$$(e.currentTarget).hide();
	}
}

//Assigning click event to the button which triggers the "next" link
$('load-more-products').observe('click', callbackFunc);

Note: In some cases, when the category has less product collections then still the button will be visible. To hide this button add the below code inside the list.phtml template file at very end.


<script>
//<![CDATA[
    //check if there are more products to be loaded or not
    if(!$$('.next.i-next')[0]){
        $('load-more-products').style.display = "none";
    }
    function testScroll(ev){
        if($$('.scroll-to-top')[0]){
            console.log(window.pageYOffset);
            if(window.pageYOffset>400){
                $$('.scroll-to-top')[0].style.display = "block";
            }
            else{
                $$('.scroll-to-top')[0].style.display = "none";
            }
        }
    }
    window.onscroll = testScroll;
//]]>
</script>

Now just hide the default page numbering group of elements using styling properties like display: none;

This worked for me on magento 1.8. I hope this works for you too as I am not doing any big changes here :)

Overriding Block or Model in magento

Here I am going to explain some points which must be considered while overriding a Mage’s php class. I am going to explain it step by step with example as overriding a model which is Mage_Review_Model_Resource_Review and the function to which I am overriding is _afterSave().

  • For overriding a class, you should create a new module (which is identical to the name of the module to which we are overriding) in app/code/local/{company_name} folder. Here {company_name} could be anything which you think would be unique throughout your project just like core files name “Mage”.
  • The created module should be registered in app/etc/modules folder.
  •     <config>
            <modules>
                <Company_Review> <!-- same like "Mage_Review" -->
                    <active>true</active>
                    <codePool>local</codePool>
                </Company_Review>
            </modules>
        </config>
    
  • Keep the directory structure of the overriding class (a model or block) same as the core class’.
  • Keep the class name same as the core class name.

    For example: If you are planning to override Mage_Review_Model_Resource_Review class then your overriding class should replace the “Mage” with {company_name}.

  •    Mage_Review_Model_Resource_Review => Company_Review_Model_Resource_Review
    
  • In the php class file, extend our created class to the overridden class and include its path from “Mage” folder.

    For example,

  • <?php 
    include_once("Mage/Review/Model/Resource/Review.php");
    class Company_Review_Model_Resource_Review extends Mage_Review_Model_Resource_Review 
    {
        protected function _afterSave()  //overriding function
        {
            //code here
        }
    }
    
  • Register your classes in local/{company_name}/{module_name}/etc/config.xml file
  • <?xml version="1.0"?>
    <config>
        <modules>     <!-- Optional, but good practice to include this -->
            <Company_Review>
                <version>1.0</version>
            </Company_Review>
        </modules>
        <global>
            <models>  <!-- would be "blocks" if you are overriding a block class -->
            	<companyreview> <!-- should be unique.. to keep it simple just add your company name to the cores class reference name -->
            		<class>Efk_Review_Model</class>   <!-- register of main class -->
            	</companyreview>
                <review>   <!-- core file's module name -->
                    <rewrite>
                        <resource_review>Efk_Review_Model_Resource_Review</resource_review>
                    </rewrite>
                </review>
            </models>
        </global>
    </config>
    

    The above layout is happened as shown in the below image:
    magento 1

    Now everything seems all set but there could be some issues related to the above config.xml file.
    Observe the below section which is excerpt from the above config.xml file

    <review>   <!-- core file's class reference name -->
          <rewrite>
                 <resource_review>Efk_Review_Model_Resource_Review</resource_review>
          </rewrite>
    </review>
    

    In the above code, the class references name which I mentioned is “review” which I am thinking to be correct after visiting the original module’s config.xml. Lets once visit the original module’s config.xml file which in our example case should be in app/code/core/Mage/Review/etc/config.xml file. The excerpt code is shown below:

    <config>
        <!-- some other code -->
        <global>
            <models>
                <review>
                    <class>Mage_Review_Model</class>
                    <resourceModel>review_resource</resourceModel>
                </review>
                <review_resource>
                    <class>Mage_Review_Model_Resource</class>
    	<!-- some other code -->
                </review_resource>
            </models>
    </config>
    

    As you can see above, there seems to be two types of class references(I don’t know what exactly they are) one is “<review>” and the other one is “<review_resource>”. As in our example we are planning to override Mage_Review_Model_Resource_Review which should be referring from “<review_resource>” instead of “<review>”. So, our overriding modules config.xml will change something like this:

    <?xml version="1.0"?>
    <config>
        <modules>
            <Company_Review>
                <version>1.0</version>
            </Company_Review>
        </modules>
        <global>
            <models>
                <companyreview>
                    <class>Company_Review_Model</class>
                </companyreview>
                <review_resource>            	
                    <rewrite>
                        <review>Company_Review_Model_Resource_Review</review>
                    </rewrite>
                </review_resource>
            </models>
        </global>
    </config>
    

    So, the correct layout is based on the following image:

    magento 2

    Now we are done and everything is perfect but sometimes still there could be issues.

    Sometimes the function which we are overriding calls a function with same name available in the parent class. So, care must be taken to call the original class’s parent class with name instead of using parent php keyword.

    For example:

    In our case, if the _afterSave() function has a line return parent::_afterSave() (which doesn’t have in real code), then call the original class’s parent class with name like Mage_Core_Model_Resource_Db_Abstract in the overrided class function. This will make sure that the overridden class is not overridden again by the original class.

    If still you can’t see the changes then re-indexing and clearing the cache will help.

    Thanks for reading my blog. Let me know if I missed something by adding comments below :)

    Some good tutorial sites

    If someone is new to any technology then everything looks dim to him/her. Many people don’t know where to start. For those people I would like to suggest to go through following steps for understanding slowly. I am not saying that it must be strictly follow. These are the steps which I always follow when learning any new technology.

    1. Go through the official website of that technology.
    2. Go through video tutorials provided by the official site or youtube.
    3. Always keep API documentation page open.
    4. Know the authors/developers and go through their sites.

    Some good tutorial sites which might help you:

    For assistance when you got any issues, ask for help on following sites:

    • Forums provided by the official site.(if any)
    • Stackexchange sites like stackoverflow.