megacolorboy

Abdush Shakoor's Weblog

Writings, experiments & ideas.

Add search functionality to your static site

If you have a static site or a blog generated using a static-site generator but want to add a simple search functionality? This could be of your interest.

I'll take you through an example on how to build a simple search engine using a JSON file and AJAX requests.

1. Generate a JSON dump of your site

Although, it's not a database but it can act as an alternative to having one. Your JSON dump can contain any metadata that you wanted your users to search in your site. In my case, I thought of allowing the user to search title and category.

Is your site generated using Python and want to create a JSON dump? Read this article for more information.

2. Build search functionality

I won't go through the aspects of UI design in this article as I feel that it's subjective and depends on one's preferences but let's keep it simple enough for this tutorial.

Before you begin writing the function, place this component in your HTML template:

<div class="searchbox">
    <input id="searchinput" type="text">
    <div id="searchresults">
        <ul></ul>
    </div>
</div>

Anyway, here's the function and you can place it directly on your template or in a separate .js file:

var _url = "path-of-your-file.json";

$(document).ready(function(){
    $('#searchinput').keyup(function(e){
        var keyword = $(this).val();
        var code = e.keyCode ? e.keyCode : e.which;

        if(code == 13){
            $.ajax({
                url: _url,
                type: "GET",
                async: false,
            }).done(function(data){
                var results = "";
                if(data.articles.length > 0){
                    $.each(data.articles, function(key, value){
                        if(v.title.search(pattern) != -1 || v.category.search(pattern) != -1){
                            results += `<li><a href="${v.slug}">${v.title}</a></li>`;
                        }
                    });
                    $("#searchresults ul").html(results);
                }
            });
        }
    });
});

That's it! Now, when you execute your script, you should be able to view your search results just like as if it were using a database.

If you want to make it similar to mine, please feel free to inspect the code on the browser or go to my repository to see how it works.

Read and Write JSON to a file in Python

You can make use of the built-in json package in Python to read and write JSON data to a file.

Writing JSON to a file

You can store the information in a dictionary or dict object, which can contain any type of data like integers, booleans, arrays or nested objects.

By using a dict object, the json package will transform your dictionary into a serialized JSON string.

import json

dataObj = {}
dataObj['posts'] = []
dataObj['posts'].append({
    'title': 'Hello world',
    'category': 'Introduction',
    'slug': 'hello-world'
})

with open('data.json', 'w') as file:
    json.dump(dataObj, file)

Reading JSON from a file

Reading is as easy as writing to a file. Using the same package again, we can parse the JSON string directly from the file.

import json

with open('data.json', 'r') as file:
    data = json.load(file)
    for item in data['posts']:
        print 'Title: ' + item['title']
        print 'Category: ' + item['category']
        print 'Slug: ' + item['slug']

I read about different package alternatives like simplejson but I guess for now, this is a great way to get started especially, if you're working with web applications and are interacting with a serialized JSON data on a daily basis.

Build a menu tree using recursion

Hmm, what is the most efficient way to build a menu tree? 😅

Most people would go for a brute force solution if it's a straightforward menu but would that be possible if we intend to create multiple levels of menu items?

Sure, you could but as the number of loops increases, the complexity of time increases too. Not to forget, you'll end up writing code that'll look messy and unscalable.

I always loved the idea and simplicity of using recursion. So, I thought of exercising my recursion skills by writing a method that can generate a dynamic menu with x number of parent and child menu items.

The following example is written in PHP:

<?php
class FooController extends Controller {
    protected $menuHTML = "";

    private function menuItems() {
        return [
            [
                'title' => 'Item 1',
                'link' => '/item-1'
            ],
            [
                'title' => 'Item 2',
                'link' => '/item-2',
                'child_items' => [
                    [
                       'title' => 'Item 2.1',
                       'link' => '/item-2.1'
                    ],       
                    [
                        'title' => 'Item 2.2',
                        'link' => '/item-2.2'
                    ],       
                ]
            ],
            [
                'title' => 'Item 3',
                'link' => '/item-3'
            ],
            [
                'title' => 'Item 4',
                'link' => '/item-4'
            ],
        ];
    }

    // Build a menu tree
    private function buildMenu($menu) {
        foreach($menu as $menuItem){
            $this->menuHTML .= '<li class="item">';
            $this->menuHTML .= '<a href="'.$menuItem['link'].'">'.$menuItem['title'].'</a>';

            // Check if it has any child items
            if(array_key_exists("child_items", $menuItem){
                $this->menuHTML .= '<ul class="submenu">';
                $this->buildMenu($menuItem['child_items']);
                $this->menuHTML .= '</ul>';
            }

            $this->menuHTML .= '</li>'
        }
    }

    // Return the complete menu
    private function getMenu($menu) {
        $this->buildMenu($menu);
        return '<ul class="mainmenu">'.$this->menuHTML.'</ul>';
    }

    public function __construct() {
        pre($this->getMenu($this->menuItems());
        die;
    }
}
?>

Once you run it, you'll see something like this:

<ul class="mainmenu">
    <li class="item"><a href="/item-1">Item 1</a></li>
    <li class="item"><a href="/item-2">Item 2</a>
        <ul class="submenu">
            <li class="item"><a href="/item-2.1">Item 2.1</a></li>
            <li class="item"><a href="/item-2.2">Item 2.2</a></li>
        </ul>
    </li>
    <li class="item"><a href="/item-3">Item 3</a></li>
    <li class="item"><a href="/item-4">Item 4</a></li>
</ul>

There a lot of ways to achieve this same result using recursion but it sure is easier to read, scalable and extensible, ain't it?

Using default function parameters in Internet Explorer

Today, I wrote a simple method that fetches images from the database via AJAX and also, I defined an empty object as a default parameter:

function fetchImages(obj={}){
    // some code here...
}

fetchImages();

The default obj parameter would contain extra parameters like id, slug and page, which would be then used to fetch a particular group of images, else, it'll fetch a random set of images.

This method worked fine in Google Chrome and Mozilla Firefox but not in Internet Explorer. I thought of inspecting the code and I was facing weird errors like undefined or Expected: ')' on the IE console.

Luckily, I had compared it to the other methods that didn't have any default parameters, so I did a little research and turns out that according to Mozilla's documentation, default function parameters are proposed by the ES6 syntax and at this point, I realized that Internet Explorer doesn't support ES6 syntax. What a bummer! 😒

However, there's a way to prevent this from happening by rewriting the method like this:

function fetchImages(obj){
    // Check if obj is defined, else make it assign it as an empty object.
    var data = obj || {};

    // some code here...
}

And BAM! The method worked flawlessly just as it's intended to do so. 😎

Addtionally, you may refer to the ECMAScript 6 Compatibility table that you might find it quite helpful to check browser compatiblity for Internet Explorer versions 11 and under.

How to identify the current target of an event?

Using the event.currentTarget property which belongs to the Event interface can help you in many ways to identify target of the current event especially if you want to fetch attributes or modify the classname of an element that belongs to a group of elements sharing the same classname.

There are many examples but I chose to write a small snippet of highlighting a tab:

function highlightTab(e){
    // Find elements that has the classname 'active' 
    // and remove them
    if(document.querySelector('div.tab_item.active') !== null){
        document.querySelector('div.tab_item.active').classList.remove('active');
    }

    // Add active class to target node
    e.currentTarget.className += " active";

    // Add active class to target's child node
    // e.currentTarget.querySelector('a.child_anchor_link').className += " active";

    // Add active class to target's parent node
    // e.currentTarget.parentNode.className += " active";
}

Read Mozilla's official documentation to know more about getting the current target of an event and it's compatibility with different web browsers.

Allow inline elements in CKEditor

Using CKEditor is awesome but I hate it when it removes inline elements like <span>, <i> or any DOM elements that contain attributes like classnames or ID, by default.

Well, CKEditor's documentation states that you can allow it by adding this line to your configuration:

var editor = CKEDITOR.replace('textarea_edit',{
    allowedContent: true,
});

After adding this line, CKEditor will stop removing those elements from your HTML content but it's also open to all tags.

You can set rules to allow only specific ones like this:

var editor = CKEDITOR.replace('textarea_edit',{
    allowedContent: 'span; i; ul; li; a[!href]'
});

Hope this helps you out! 😊

Check if the current route exists before fetching it's parameters

If you've ever come across this type of error when you're trying to fetch parameters of the current route:

Symfony\Component\Debug\Exception\FatalThrowableError: Call to member function parameters() on null

It's probably because the route doesn't exist, which is why it failed to call the parameters() function. It can be easily resolved by checking if the route exists before calling the function:

<?php
namespace App\Http\Controllers;
use Route;

class FooController extends Controller {
    public function __construct(Request $request){
        // insert code here...
    }

    public function foo(Request $request){
        $params = $request->route() ? Route::current()->parameters() : '';
        return $params;
    }
}
?>

Although, the sample code above is to fetch parameters of the current route, you can apply this before calling any method from the Route class.

This works for version 5.2 and above.

Queues & Stacks

Let's look at the differences between the two data structures:

  1. Queues: First-In First-Out
  2. Stacks: Last-In Last-Out

Queues

This follows a First-In First-Out processing order i.e. the first element added to a queue will be processed first. A queue should support two operations:

  • Enqueue
  • Dequeue

Enqueue

Adds the element to the tail of a queue. The tail position gets incremented.

Dequeue

Removes the first element of a queue i.e. the head element. Once, it's removed, the subsequent element becomes the new head element of the queue. The position of the new head element gets incremented and the previous one is assigned a negative integer like -1 or some garbage value.

Implementation of a standard queue using C++:

class Queue {
    private:
        int pos;
        vector<int> data;

    public:
        Queue() {
            pos = 0;
        }

        bool enqueue(int value) {
            data.push_back(value);
            return true;
        }

        bool dequeue() {
            if(isEmpty()){
                return false;
            }
            pos++;
            return true;
        }

        int front() {
            return data[pos];
        }

        bool isEmpty() {
            return pos >= data.size();
        }
}

In terms of memory management, a standard Queue is quite inefficient and incapable of handling dynamic memory.

Stacks

This follows a Last-In First-Out processing order i.e. the last element added to a queue will be the first to be removed. Just like queues, it has two simple operations:

  • Push
  • Pop

Push

Each element is pushed towards the end of the stack. Think of it as a card deck where you stack a card on top of another card.

Pop

It removes the most recent element i.e. the newly added element from the stack.

Implementation of a stack using C++:

class Stack {
    private:
        vector&ltint> data;
    public:
        void push(int value) {
            data.push_back(value);
        }

        bool isEmpty() {
            return data.empty();
        }

        int top() {
            return data.back();
        }

        bool pop() {
            if(!isEmpty()) {
                data.pop_back();
                return true;
            }
            else {
                return false;
            }
        }
}

Unlike queues, stacks are easier to implement and pretty efficient at managing dynamic memory.

Oh, if you ever get to use these, don't worry about implementing them, nearly all programming languages have their own implementations of stack and queue that comes with it's own standard library.