📜 ⬆️ ⬇️

Using objects for beautiful code structure in javascript

Introduction


Good all the time of day. I congratulate you on holidays and turn to the topic.
When you go beyond writing simple jQuery snippets and start developing more complex user interactions, your code can quickly become cumbersome and difficult to debug. This article will show you to start thinking about these interactions in terms of “behavior bits” using the object literal design pattern.

In the past few years, JavaScript libraries have given novice developers the ability to add additional interaction with the site. Some of them, such as jQuery, have a syntax so simple that people with zero programming experience have learned how to quickly add “whistles and bells” to their pages. You just need to google the plugin, copy and paste a few dozen lines from the user code. The result - the client is impressed, and you add jQuery to your resume.

But wait. Suppose now the requirements have changed. Now the code that worked for the three elements should work for ten. Or your code should be reused for a slightly different application, in which all identifiers are different. How not to get lost in the code, which is not just a call to a plugin or a couple of lines like show () and hide ()?

Get acquainted with Object Literal


Trying to find a definition of this term in Russian, I came across the following Wikipedia article. I would be grateful if someone will help me to give a more precise definition.
')
The Object Literal programming pattern is a way of organizing code that makes you think at the very beginning of what your code will do and which parts should be in place in order to do this. This is also a good way to ensure that your code does not “pollute the global namespace”, which is good practice for all projects (especially for large projects). Object Literal is also a way to encapsulate behavior.
var myObjectLiteral = {
myBehavior1 : function () {
/* - */
},

myBehavior2 : function () {
/* */
}
};


* This source code was highlighted with Source Code Highlighter .

As an artificially simplified example, I’ll give a jQuery piece to show or hide content when clicking on a list item:
$( document ).ready( function () {
$( '#myFeature li' )
.append( '<div/>' )
.each( function () {
$( this ).find( 'div' )
.load( 'foo.php?item=' + $( this ).attr( 'id' ));
})
.click( function () {
$( this ).find( 'div' ).show();
$( this ).siblings().find( 'div' ).hide();
});
});


* This source code was highlighted with Source Code Highlighter .

Simple enough. And yet, even in this example, there are several things that you may want to change later - for example, the method of setting the URL to download the content, where to download the received content, etc. In the Object Literal view, functions clearly distinguish these aspects. It might look like this:
var myFeature = {
config : {
wrapper : '#myFeature' ,
container : 'div' ,
urlBase : 'foo.php?item='
},

init : function (config) {
$.extend(myFeature.config, config);
$(myFeature.config.wrapper).find( 'li' ).
each( function () {
myFeature.getContent($( this ));
}).
click( function () {
myFeature.showContent($( this ));
});
},

buildUrl : function ($li) {
return myFeature.config.urlBase + $li.attr( 'id' );
},

getContent : function ($li) {
$li.append(myFeature.config.container);
var url = myFeature.buildUrl($li);
$li.find(myFeature.config.container).load(url);
},

showContent : function ($li) {
$li.find( 'div' ).show();
myFeature.hideContent($li.siblings());
},

hideContent : function ($elements) {
$elements.find( 'div' ).hide();
}
};

$( document ).ready( function () { myFeature.init(); });


* This source code was highlighted with Source Code Highlighter .

Since the initial example is incredibly simple, Object Literal is larger in size. In truth, the Object Literal method will not save you lines of code, but it will help you avoid a headache. Using Object Literal, we have broken our code into logical parts in order to more easily find what we may want to change in the future. We have made our code extensible by providing the ability to pass on to override the default configuration. We also made some kind of self-documentation - now it’s easier to get into what our piece of code does. As your needs grow beyond this example, the benefits of the Object Literal approach will become increasingly apparent (see below).

One more example


Our task will be to create an element of the user interface, which is content divided into sections. Clicking on the section will show a list of subsections. By clicking on a subsection in the navigation menu, the corresponding content will appear in the content area. When you click on a section, its first subsection should be displayed. The first section should be displayed while the page is loading ( this should work ).

Step 1: HTML structure

Writing good semantic HTML is essential for writing good JavaScript code, so let's think about what HTML might look like for our task. HTML should:

Given these principles:
< div id ="myFeature" >
< ul class ="sections" >
< li >
< h2 >< a href ="/section/1" > Section 1 </ a ></ h2 >
< ul >
< li >
< h3 >< a href ="/section/1/content/1" > Section 1 Title 1 </ a ></ h3 >
< p > The excerpt content for Content Item 1 </ p >
</ li >
< li >
< h3 >< a href ="/section/1/content/2" > Section 1 Title 2 </ a ></ h3 >
< p > The excerpt content for Content Item 2 </ p >
</ li >
< li >
< h3 >< a href ="/section/1/content/3" > Section 1 Title 3 </ a ></ h3 >
< p > The excerpt content for Content Item 3 </ p >
</ li >
</ ul >
</ li >

< li >
< h2 >< a href ="/section/2" > Section 2 </ a ></ h2 >
< ul >
< li >
< h3 >< a href ="/section/2/content/1" > Section 2 Title 1 </ a ></ h3 >
< p > The excerpt content for Content Item 1 </ p >
</ li >
< li >
< h3 >< a href ="/section/2/content/2" > Section 2 Title 2 </ a ></ h3 >
< p > The excerpt content for Content Item 2 </ p >
</ li >
< li >
< h3 >< a href ="/section/2/content/3" > Section 2 Title 3 </ a ></ h3 >
< p > The excerpt content for Content Item 3 </ p >
</ li >
</ ul >
</ li >

< li >
< h2 >< a href ="/section/3" > Section 3 </ a ></ h2 >
< ul >
< li >
< h3 >< a href ="/section/3/content/1" > Section 3 Title 1 </ a ></ h3 >
< p > The excerpt content for Content Item 1 </ p >
</ li >
< li >
< h3 >< a href ="/section/3/content/2" > Section 3 Title 2 </ a ></ h3 >
< p > The excerpt content for Content Item 2 </ p >
</ li >
< li >
< h3 >< a href ="/section/3/content/3" > Section 3 Title 3 </ a ></ h3 >
< p > The excerpt content for Content Item 3 </ p >
</ li >
</ ul >
</ li >

</ ul >
</ div >


* This source code was highlighted with Source Code Highlighter .

Note that we did not include markup to display section navigation; these parts will be added to jQuery, since they will only work with jQuery; users without javascript will get just beautiful semantic markup. (If anything from here seems surprising or confusing, you can google POSH (plain-old semantic HTML) and progressive enhancement).

Step 2: Preparing the object

My first step in creating an object for a task is to create “stubs” for an object. Stubs are placeholders that help in planning the structure of the code. Our object will have the following methods:
var myFeature = {
'config' : { },
'init' : function () { },
'buildSectionNav' : function () { },
'buildItemNav' : function () { },
'showSection' : function () { },
'showContentItem' : function () { }
};


* This source code was highlighted with Source Code Highlighter .


Here it is worth noting myFeature.config , which stores in one place the default values. We will add the ability to override the default values ​​when we define the myFeature.init () method.

Step 3: Code

After we built this skeleton, you can start writing code. Let's start by creating a simple myFeature.config object and method myFeature.init () :
'config' : {
// #myFeature
'container' : $( '#myFeature' )
},

'init' : function (config) {
// init()
if (config && typeof (config) == 'object' ) {
$.extend(myFeature.config, config);
}

// / DOM
//
myFeature.$container = myFeature.config.container;

myFeature.$sections = myFeature.$container.
//
find( 'ul.sections > li' );

myFeature.$section_nav = $( '<p/>' )
.attr( 'id' , 'section_nav' )
.prependTo(myFeature.$container);

myFeature.$item_nav = $( '<p/>' )
.attr( 'id' , 'item_nav' )
.insertAfter(myFeature.$section_nav);

myFeature.$content = $( '<p/>' )
.attr( 'id' , 'content' )
. insertAfter(myFeature.$item_nav);

//
// ""
myFeature.buildSectionNav(myFeature.$sections);
myFeature.$section_nav.find( 'li:first' ).click();

// HTML
myFeature.$container.find( 'ul.sections' ).hide();

// ( )
myFeature.initialized = true ;
}


* This source code was highlighted with Source Code Highlighter .

Next, create the myFeature.buildSectionNav () method:
'buildSectionNav' : function ($sections) {

//
$sections.each( function () {

var $section = $( this );

//
$( '<li/>' )
// h2
//
.text($section.find( 'h2:first' ).text())

//
.appendTo(myFeature.$section_nav)

// data()
.data( 'section' , $section)

// click
.click(myFeature.showSection);
});


* This source code was highlighted with Source Code Highlighter .

Next, create a method myFeature.buildItemNav() :
'buildItemNav' : function ($items) {
$items.each( function () {

var $item = $( this );

//
$( '<li>' )

// h3
//
.text($item.find( 'h3:first' ).text())

// add the list item to the item navigation
.appendTo(myFeature.$item.nav)

// data()
.data( 'item' , $item)

// click
.click(myFeature.showContentItem);
});

* This source code was highlighted with Source Code Highlighter .

Finally, we will write methods for displaying sections and content items:
'showSection' : function () {
var $li = $( this );

//
myFeature.$item_nav.empty();
myFeature.$content.empty();

// jQuery
// data() buildSectionNav
var $section = $li.data( 'section' );

//
$li.addClass( 'current' )
.siblings().removeClass( 'current' );

//
var $items = $section.find( 'ul li' );

//
myFeature.buildItemNav($items);

// ""
myFeature.$item_nav.find( 'li:first' ).click();

},

'showContentItem' : function () {
var $li = $( this );

//
$li.addClass( 'current' )
.siblings().removeClass( 'current' );

// jQuery
// data() buildSectionNav
var $item = $li.data( 'item' );

//
myFeature.$content.html($item.html());
}


* This source code was highlighted with Source Code Highlighter .


All that's left to do is call myFeature.init() :
$( document ).ready(myFeature.init);

* This source code was highlighted with Source Code Highlighter .

You can look at the whole picture here (+ some CSS for the view).

Step 4: Change Requirements

No project is complete without last minute requirements changes, right? Object Literal approach helps to make the necessary changes quickly and painlessly.

What if we need to extract the content of the excerpt item using AJAX, not from HTML? Except for the backend setting:
var myFeature = {

'config' : {
'container' : $( '#myFeature' ),

//
// URL
'getItemURL' : function ($item) {
return $item.find( 'a:first' ).attr( 'href' );
}

},

'init' : function (config) {
//
},

'buildSectionNav' : function ($sections) {
//
},

'buildItemNav' : function ($items) {
//
},

'showSection' : function () {
//
},

'showContentItem' : function () {

var $li = $( this );

$li.addClass( 'current' ).
siblings().removeClass( 'current' );

var $item = $li.data( 'item' );
var url = myFeature.config.getItemURL($item);

// myFeature.$content.html($item.html());
myFeature.$content.load(url);

}

};


* This source code was highlighted with Source Code Highlighter .

Need more flexibility? There are a lot of things you can customize (and, therefore, override). For example, you can use myFeature.config to specify how to find and process the title text for each navigation item:
var myFeature = {
'config' : {
'container' : $( '#myFeature' ),

'itemNavSelector' : 'h3' ,

'itemNavProcessor' : function ($selection) {
return 'Preview of ' +
$selection.eq(0).text();
}
},

'init' : function (config) {
//
},

'buildSectionNav' : function ($sections) {
//
},

'buildItemNav' : function ($items) {

$items.each( function () {
var $item = $( this );

var myText = myFeature.config.itemNavProcessor(
$item.find(myFeature.config.itemNavSelector)
);

$( '<li/>' )

.text(myText)
.appendTo(myFeature.$item_nav)
.data( 'item' , $item)
.click(myFeature.showContentItem);
});
},

'showSection' : function () {
//
},

'showContentItem' : function () {
//
}

};


* This source code was highlighted with Source Code Highlighter .

After you add a default configuration object, you can override it when you call myFeature.init ()
$( document ).ready( function () {
myFeature.init({ 'itemNavSelector' : 'h2' });
});


* This source code was highlighted with Source Code Highlighter .

Conclusion


If you looked at the code examples in this article, you should have a general understanding of Object Literal, and how you can use this approach to create more complex interactions.

An example is taken from here .

additional literature

Source: https://habr.com/ru/post/111290/


All Articles