4elements, Amsterdam, Holland

  1. Adding a Custom Product Type in Magento

    Sometimes the available product types just aren’t enough. Magento has the different types pretty well covered, but there’s just situations where it simply makes more sense to have a separate product type. 

    For me, one of those situations was when I needed to create a custom product module that would handle generating a customized PDF based on the options of the product that was purchased.

    Additionally, this is not a solution if you need to create a product with a specific attribute since Magento already provides these features. Instead, you should find this guide handy for when (or if) you need to be able to identify a specific type of product anywhere in the system. 

    For example, like when hooking observers into it.

    Set Up the Module

    This guide is going to assume you have some basic knowledge of the inner workings of a Magento module. Building a good module is a bit of an art on its own and it's the subject matter of a post all its own. With that said, we're going to take a look at how to set up the module for it.

    Now, on to the main point of discussion.

    Like everything in Magento, our work here too starts in the configuration files. We want to create a separate module for this because in Magento, we want separate modules for everything. Keeping things separate and modular in Magento keeps us happy. I ended up with Robogento Printable, Robogento being my company and Printable the module name.

    To add a product type, we have to tell Magento about it and define it. These are the applicable parts of the config.xml file we’re going to need, you will need to fill in the blanks yourself, of course:

    <config>
      ...
      <global>
        ...
        <catalog>
          <product>
            <type>
              <printable translate="label" module="printable">
                <label>Printable Product</label>
                <model>printable/product_type</model>
                <price_model>printable/product_price</price_model>
                <is_qty>1</is_qty>
              </printable>
            </type>
        ...

    There we go. 

    As you can see, my module is defined as “printable”, the label is for the benefit of filtering in the grids that show the product in the backend and when creating a new product. The model and price model are where the fun begins. Every product needs a type, this can be pretty straight forward.

    Extending Magento

    Extend the base system by creating the Type model. Watch the file path based on the class definition:

    class Robogento_Printable_Model_Product_Type
        extends Mage_Catalog_Model_Product_Type_Simple { }

    And there we go. 

    Suddenly our printable product is now a clone of the Simple Product Magento is already aware of. What’s actually happening here is that by extending from the Simple Type, we’re reusing all of the features that product type has. The added benefit is we can very simply override the existing features with our own if we’re so inclined.

    There was one other thing I showed you for our config.xml file. The product type’s price model. Very much the same thing applies here:

    class Robogento_Printable_Model_Product_Price
        extends Mage_Catalog_Model_Product_Type_Price { }

    As you can see, we’re simply extending from Magento’s existing system again. Magento has flaws enough, but the simple and virtual product types were among the better thought out parts of it and it’s always a good idea to reuse existing code when you can.

    Normally, I'd say there's more to it, but in the case of Magento, this is really all there is to it. We’ve now essentially created an alias of the Simple Product system and named it Printable. 

    The printable product will use all of the features and definitions as they are set up for the simple products. So two addresses, inventory settings, you name it If you only need an invoice address, extend from the Virtual product instead. If you want to provide downloadable products, but with a slightly different set of features, extend from the Downloadable system.

    What to Keep in Mind

    Like I said at the start, this is not to create a T-Shirt type in your new Magento store. In that case, you’re most probably looking for the attribute sets in the backend.

    Using this, you'll be able to quickly filter out specific products, be it in the backend or when using other Magento features like observers or collections.

     

    0 Comments

    Leave a comment › Posted in: Daily

    1. Call for Authors: Write For Tuts+!

      We're currently looking for more authors to join our team! Specifically, we're looking for those who have strong web development skills - both front-end and back-end. 

      Of course, this raises the question: what all does this entail when it comes to write for an established, respected, and educational network?

      Depending on on your level of experience, it could mean a variety of different things.

      The Skills We're Looking For

      We aim to publish content on Tuts+ Code that focuses specifically on web development technology. A broad topic, right?

      This includes anything and everything from PHP, Ruby, server-side JavaScript, HTML5, CSS3, document-based database systems, and everything in between. No matter what you're familiar with be it in your day-to-day, your hobby work, or both, we're likely looking to run it.

      After all, our ultimate goal is to help others earn and learn.

      Of course, what's the bottom line for you guys and girls who are interested in writing for us? Take a look at some of the benefits.

      Becoming a Tuts+ Author

      Some of the benefits include:

      • Writing about a topic and educating others about a topic that you're specifically interested in not only furthering your skills, but also in teaching others.
      • Getting your name out into the community especially if you're starting your freelance career.
      • You're an established developer or consultant and are looking to diversify your platform while expanding your writing skills.
      • Establish yourself as an expert in your given field.
      • Collaborate with other authors on topics, projects, and more!

      There are many more benefits that come with this position some of which have kept a number of our authors around for quite some time, and that have lead them into other opportunities, as well!

      Let's Talk Money!

      Down to brass tacks, one of the biggest questions that people have is "How much will I get paid?" Some of our regular authors bill us for $700 USD+ per month for publishing high quality tutorials, several times per month.

      Our rates start at $100 USD per quick tip tutorial and $250 USD per regular tutorial (that is, per article per tutorial). We are willing to discuss rates for higher profile authors and educators, as well.

      If you're a regular author who is consistently hitting deadlines, providing quality content and engaging with readers, you can expect raises and to bill more as you continue to work with us. Earn money from home teaching topics that you love working with a distributed team. Not bad, huh?

      Pitch a Tutorial!

      We'll need the following information from you:

      • Name.
      • A brief paragraph about your background and why you're a good fit.
      • A link to an article that you've written, helped create, or like.
      • Two ideas for tutorials that you'd like to write for us.
      • Examples of your work, either attached images or a link to your portfolio.

      E-mails with just a couple of sentences will be discarded... we want passionate people teaching our passionate readers! Send the above using this form.

      I look forward to hearing from you and can't wait to see what you bring to the table, to work with you, and for helping us to further educate our readers!

       

      0 Comments

      Leave a comment › Posted in: Daily

    1. Unleashing the Power of Magento Newsletters

      Perhaps one of the most overlooked functionality of Magento is its Newsletter feature. Magento is one of very few CMSs which offers built-in user subscription, management and newsletter sending functionality. Properly using these features can save lot of time and money, which would otherwise be spent on integrating third-party applications for these features. 

      Store owners can ask store visitors to subscribe to their newsletter and send them latest and special offers, discount coupons which could result in repeating sales and minimizing marketing cost per sale.

      Effective placement of the user subscription form in the website is the linchpin of any successful email marketing strategy. Some commonly used places for placing subscription form are sidebar and the footer. Some store owners also place this subscription form on prominent places of home page, some even place it in a lightbox. Mentioning some attractive offer for your newsletter subscribers can substantially increase subscribers’ opt-in rate. 

      Creating a Newsletter Template

      Once you have some subscribers in your list, its time to start exploring Magento newsletter feature by first creating a Newsletter template. To create a new template go to Magento admin panel, and from the upper menu hover over ‘Newsletter’ option, from the dropdown, click on ‘Newsletter Templates.’

      On the next page you’ll see all the available templates created so far, and the link to create a new template. As we haven’t created any templates yet, that’s why it is showing ‘No record found’ in the given screenshot. Click on the ‘Add New Template’ link to start creating a new template.

      On the next page you’ll see a detailed form showing various fields to create a new template, as shown in screenshot below.

      • Template Name: In the template name field, enter the name for this template. This name will not be visible anywhere in the emails; so name it the way you want.
      • Template Subject: This will be the default subject of the emails sent using this template. You can edit this subject at the time of sending emails.
      • Sender Name: This name will be shown to users as the sender of the email.
      • Template Content: This is the most important part of the template. You can insert template content here. You can either insert your own HTML here if you have some proficiency in HTML, otherwise you can use WYSIWYG editor to create a simple template for yourself. If you want to create some fancy looking template, I would recommend using either paid newsletter templates from Themeforest, or free templates from Zurb.

      However, if you plan to create your own template, remember to use the best practices of creating email width under 600px, also it should be table-based HTML with inline CSS not because tables are the best elements, but because they transfer best across all clients. 

      Also, while creating your own template, leave the default code shown in content field at the bottom of the email. It’ll generate dynamic unsubscribe link, which is necessary to abide by spam rules.

      • Template Styles: The last field is for inserting template CSS. Though, as mentioned above the best practice is to use inline CSS while creating email templates, however if you want to insert any CSS style separately, this is the place to put it.

      Once you are all done creating this template, make sure to preview your template by using the ‘Preview Template button’ in the upper right corner. Once you are sure you are all set, hit the ‘Save Template’ button.

      Now go back to Newsletter template page, and here you’ll see the template we have just created.

      In the last column of the newsletter template, you’ll see a drop-down to select action for this template. Select ‘Queue Newsletter’ from this drop down.

      Upon selecting ‘Queue Newsletter’ option, a new page will load, similar to template creation page, this time it’ll have two new fields there. One is a select form, where you can select the subscribers from different stores.

      The other option is that of selecting date and time to start the queue.

      The Subject, Sender Name, Sender Email, Message and Styles field will be pre-populated with the template content we just created. You can edit the content here, and click on ‘Save Newsletter’ in upper right corner. Do not forget to preview the email, before saving this newsletter. Once you save this newsletter, it'll be scheduled, you can edit/delete it before scheduled sending time, if needed.

      You can view the queued emails by clicking on ‘Newsletter’ ->‘Newsletter Queue’ from the top menu.

      Here you’ll see all the queued emails.

      At the queue start time, Magento will start sending emails in batches of 15-20. If anything goes wrong while sending these emails, they’ll be reported in Newsletter->Newsletter Problem Reports.

      This was a quick overview of utilizing this built-in feature of Magento. There are plenty of extensions available at Magento connect, which’ll further enhance Newsletter functionality. Some top rated extensions in this regard are Advanced Newsletter by aheadWorks and Advanced Subscribers by apptha

      You can use different extensions to integrate Magento with other newsletter sending applications like MailChimp, and transactional email services like Sendgrid etc.

      Conclusion

      On parting thoughts, I would say that coupling this useful feature of Magento with some good extensions can fulfill almost all your Newsletter needs. So, do give Magento Newsletter feature and related extensions a careful look, before you embark on buying some 3rd party Newsletter sending and management application.

       

      0 Comments

      Leave a comment › Posted in: Daily

    1. Authorization and Protecting Web Resources in ASP.NET

      Giving each web site user an individual account allows you to uniquely identify users of your web site and validate that they are who they claim to be. Knowing a user’s identity allows the web site to change to reflect the needs and interests of each user. As web sites normally contain multiple sections designed for users in different roles from general public to high level system administrators, you can also use this identification to manage access to the different resources on the web site that each users needs.

      On an internal corporate intranet, there may be pages containing sensitive reports and data that should only be seen by specific departments or senior managers and not every employee of the company. In most cases, only certain administrators would have full access to change setting of the web site with other users perhaps being granted less change ability. In all these cases, we need to restrict these options for most users while allowing them to others. Authorization lets you ensure a user can access everything they need on a web site and perform desired tasks, but no more.

      For example, on a e-commerce site, you would want customers to browse products, add products to a cart, and order those products. Once ordered they should be able to track their order, and possibly modify the order before shipping. However you also might want new customers to speak to a customer service agent before allowing them to change an order. In no case would you want any customer able to view, change, or cancel orders of other customers.

      As a web developer, an important security aspect of a site them comes in ensuring that users do not have access to actions they should not perform. The consequences of not protecting it can be serious

      Let’s look at an overview of authorization and how to implement it in ASP.NET.

      What is Authorization?

      Giving unique accounts to individuals allows us identify who accesses our web site. Trusting the site knows who an individual is authentication and was previously discussed.

      Once we know the user, we can tailor the site for that user, and customize the web site to reflect information that we know about the user. This can vary from simple aspects such as a web site stating, “Hi Bill,” when I log in. 

      Most commerce sites can save my credit card, address, and other information I need to place an order. This means I don’t need to enter the same information each time, which makes it easier for me to place and order and therefore more likely to order from them. Web sites can also take this further using my history and habits to suggest related products that I might be interested in or similar articles that I might enjoy reading. I might also be able to set preferences in colors, preferred categories, or other settings the site can use.

      That may provide helpful benefits to the site’s users, but authorization focuses on using the unique identity of the user to to determine what actions the user can perform. It lets the web site determine if a user should have the ability to access different sections of a web site, be able to access data, or be able to make changes to data. 

      This use of the identity will be the primary focus of this article as we look at methods of protecting portions of your web site, focusing on ASP.NET.

      Controlling Access Using Groups and Roles

      While it is possible to provide unique rights and responsibilities for each user to a web site, this quickly becomes unmanageable as the number of users grow. Quickly, the chance of mistakes increases with each new user needing custom setup. If any change in the site required new rights or settings, then each user account would need to be updated possibly requiring manual updates to hundreds or thousands of accounts.

      For this reason. users are normally grouped together with those having similar rights or needs. The groups are often sometimes referred to as roles since the role of the user in a site often defines the groups used. For each group the site administrator can define access and restrictions within the web application. 

      You then assign users to these groups and the user will take on the rights and restrictions assigned to that group. The rights can be taken away simply by removing the user from the group. Most systems support a user being in more than one group at the same time as a user may have multiple roles.

      Users in multiple roles requires a method of dealing with cases where the settings of two groups conflict. For example take a user that is a member of two groups. One group allows the user to create a new blog post and the second denies this ability to the user. The web site must handle this conflict in a consistent and predictable way. In almost every case, the best practice allows no rights by default, adds only specifically listed rights, and to let deny override other settings. In that case the group denying the right would override the group denying access.

      A modification of splitting users into groups based around roles would be to create groups based on the activity. In the first case, you might have “authors,” “editors,” “publishers,” etc. In the second you might have groups for “create article,” “edit article,” “delete article,” “publish article.” This method gives more flexibility in exchange for managing more groups.

      Protecting Pages in ASP.NET Web Sites

      Your first concern should be protecting the web pages on your site. I focus on ASP.NET for the specifics in this article, but most web frameworks use similar concepts though not the same files and commands. Depending on the system there are three approaches to secure and ASP.NET web site: 

      1. ASP.NET routing
      2. ASP.NET web forms
      3. ASP.NET MVC

      Protecting Web Forms ASP.NET Sites

      ASP.NET routing and ASP.NET web forms utilize the web.config file to secure web page access. A basic configuration to secure access a resource on a web site would look similar to the following:

      <configuration>  
         <location path="adminhome.aspx">  
            <system.web>  
               <authorization>  
                 <allow roles="admin"/>  
                 <deny users="*"/>  
               </authorization>  
            </system.web>  
         </location>  
      </configuration>

      The location element of this XML snippet defines the path to the file, folder, or route that we’re dealing with. Here we’re specifying this applies to the adminhome.aspx page specified. This could also give a folder on the site and would apply to that folder. If you specify no path attribute the configuration settings apply to the current directory of this web.config file and all child directories.

      The authorization element contains the settings used to set up who has access and who is denied access to the object specified in the path element. The rules are checked starting with the first rule in order until a match is found. The allow element specifies roles and/or users who will be granted access to the resource. Similarly the deny element specifies users and roles that will not be allowed to access the resource.

      In this example, the <allow admin role/> rule will be checked first. If the user is in the admin role, then they are granted access and nothing more needs to be checked. If the user isn’t in that role, then ASP.NET continues to the next rule. Here, that <deny users="*"/> rule would deny all users. This example therefore would allow users in the admin role access. All other users would be denied access.

      There are a few special characters to specify common groups. We saw the * user above, which specifies all users. The ? user refers to anonymous users, that is any user that has not currently logged in. Multiple users and roles can be specified separating them with a comma. Users and roles can be mixed in the same rule such as:

      <allow roles="siteadmin,editors" users="bob">

      Protecting ASP.NET MVC Sites

      ASP.NET MVC focuses on controllers and actions on those controllers instead of files. This changes the method of securing access to an ASP.NET MVC site. By default, all actions and controllers can be accessed by all users, just as in WebForms. You still use the same role and user attributes, but no longer set these within the web.config file. 

      Instead you apply an [Authorize] attribute to your controllers and actions directly. As an example if you have an AdminController that should only be accessed by members of the admin role, you can do that by adding the users and/or roles to the tag. Note this acts as a allow with an implied deny for all not specifically allowed.

      [Authorize(Roles = "siteadmin")]  
      public class AdminController : Controller  
      {
      ...

      The same * and ? options for all users and anonymous users are also available for this attribute. You can apply the rules specifically to an individual action on the controller to restrict only those actions. Attributes specified on an action will override those specified for the entire controller.

      [Authorize(Roles = "siteadmin")]  
      public ActionResult AdminView()  
      {
      ...

      If you do not specify any roles or users with the [Authorize] attribute, then it will allow any authenticated user to log in. This allows you to only allow access to actions or controllers for users that are specifically logged into the system. 

      ASP.NET 4 added an [AllowAnonymous] attribute that allows you to override this for an action within a controller. You can find it used in any new default ASP.NET MVC Internet Projects to manage access to the AccountController controller.

      Managing Pages Used by Multiple Roles

      Once you have protected access to the folders, files, actions, and routes on your site, you next need to look at ensuring proper access within server code itself. Some pages are simple to secure in that only a single role should access or see them at all and those users have the ability to do anything provided on the page.

      For many pages, different roles may access the same page, but have different rights and abilities once on the page. In these cases, take care not to show links to actions, URLs, or files the current user does not have the right to access. 

      There is no value in showing the link to the administration area to a user without admin access or a "Refund Order" button to a user who doesn’t have that ability. Even if the button or link is inactive, it provides potential information to an attacker. It can also cause confusion for the site’s legitimate users. If the link is active, but then requests a login, you’ve provided an attacker with a page to target, and again possibly confused a legitimate user of the site.

      Server code behind a page accessed by users in multiple roles should always validate the rights of the user before performing an action. If both admins and anonymous users can access a page, you should validate the user is in an admin role before performing actions only the admin role can do. The user could attempt an action they should not be able to perform either through clicking of a link that shouldn’t have been shown, experimentation, or a deliberate hacking attempt. 

      Again always assume the least privilege and require explicit granting through group membership, roles, or other elements before performing secured functions.

      Also take care if the action is passed as a parameter to a page. Take a URL that completes an order in the form of UpdateOrder.aspx?order=33&action=delete. Image a hacker tried accessing other actions at random until discovering UpdateOrder.aspx?order=33&action=refund would credit the charge for the order back without canceling the order. Never rely of a link being hidden or not shown as the sole defense mechanism against unauthorized actions.

      Security Aspects of User Sessions

      While the authentication aspect differs from the authorization element discussed here, they are interrelated. First, at the login session usually are set with a timeout in the configuration. In ASP.NET, this is set in the web.config file in the <authentication> section.

      <forms loginUrl="~/Auth/LogOn.aspx" timeout="30" slidingExpiration="true" />

      This would set the timeout for a user to 30 minutes. The slidingExpiration attribute determines if a request resets this counter back to zero. With it set false, then a user would be required to log back in every thirty minutes even if actively using the site that entire time.

      Also be aware of the risk of session hijacking. Most web frameworks use a unique identifier for the user once authenticated, normally stored in a cookie. If this cookie isn’t protected in some way then anyone who can view the user’s traffic can use that cookie to pass themselves off as the original user. 

      The FireSheep Firefox extension demonstrated this and provided a simple method to perform this interception and impersonation. You can only prevent this by using SSL encryption of the entire web browser session or at least protecting the cookie containing the data with SSL encryption.

      You can protect against this using SSL only cookies for the authentication token representing the logged in user. This ensures the cookie is only sent when the page is accessed by SSL. 

      In ASP.NET, you can enforce this by setting the requireSSL="true" attribute on the <forms/> portion of the web.config when using forms authentication. For greater protection, you can also set the <httpCookies requireSSL="true" /> item in your web.config to set all cookies to by SSL only by default.

      Conclusion

      The use of web sites by many users with different needs and responsibilities requires methods to prevent unauthorized access to sensitive data and functionality. You can use the unique identity of a user to determine which rights the user has and enforce those rights within your web application.

      You begin by ensuring that pages and actions within your web application are restricted to only those users who should have the ability to work with them. 

      For pages accessed by users in multiple roles you should take care to validate the user has the right to perform requested actions before performing them. Since identity of the user defines their access, you should also take care to ensure that others cannot impersonate a user with more rights. 

      Combining these steps will go a long way toward protecting your web application.

       

      0 Comments

      Leave a comment › Posted in: Daily

    1. Bubble.js: A 1.6K Solution to a Common Problem

      One of the most common tasks in web development is event management. Our JavaScript code is usually listening to events dispatched by the DOM elements. 

      This is how we get information from the user: That is, he or she clicks, types, interacts with our page and we need to know once this happen. Adding event listeners looks trivial but could be a tough process.

      In this article, we will see a real case problem and its 1.6K solution.

      The Problem

      A friend of mine works as a junior developer. As such, he doesn't have a lot of experience with vanilla JavaScript; however, he has had to start using frameworks such as AngularJS and Ember without having the fundamental understanding of DOM-to-JavaScript relationship. During his time as a junior developer, he was put in charge of a small project: A single page campaign websites with almost no JavaScript involved. He faced a small but very interesting problem which ultimately lead me to write Bubble.js.

      Imagine that we have a popup. A nicely styled <div> element:

      <div class="popup"> ... </div>

      Here is the code that we use to show a message:

      var popup = document.querySelector('.popup');
      var showMessage = function(msg) {
          popup.style.display = 'block';
          popup.innerHTML = msg;
      }
      ...
      showMessage('Loading. Please wait.');

      We have another function hideMessage that changes the display property to none and hides the popup. The approach may work in the most generic case but still has some problems. 

      For example, say we need to implement additional logic if the issues come in one by one. Let’s say that we have to add two buttons to the content of the popup - Yes and No.

      var content = 'Are you sure?<br />';
      content += '<a href="#" class="popup--yes">Yes</a>';
      content += '<a href="#" class="popup--no">No</a>';
      showMessage(content);
      

      So, how we will know that the user clicks on them? We have to add event listeners to every one of the links. 

      For example:

      var addListeners = function(yesCB, noCB) {
          popup.querySelector('.popup--yes').addEventListener('click', yesCB);
          popup.querySelector('.popup--no').addEventListener('click', noCB);
      }
      showMessage(content);
      addListeners(function() {
          console.log('Yes button clicked');
      }, function() {
          console.log('No button clicked');
      });

      The code above works during the first run. What if we need a new button, or even worse, what if we need a different type of button? That is, what if we were to continue using <a> elements but with different class names? We can't use the same addListeners function, and it's annoying to create a new method for every variation of popup.

      Here are where problems become visible:

      • We have to add the listeners again and again. In fact, we have to do this every time when the HTML in the popup’s <div> is changed.
      • We could attach event listeners only if the content of the popup is updated. Only after the showMessage calling. We have to think about that all the time and sync the two processes.
      • The code that adds the listeners has one hard dependency - the popup variable. We need to call its querySelector function instead of document.querySelector. Otherwise, we may select a wrong element.
      • Once we change the logic in the message we have to change the selectors and probably the addEventListener calls. It is not DRY at all.

      There must be a better way to do this.

      Yes, there is a better approach. And no, the solution is not to use a framework.

      Before to reveal the answer let’s talk a bit about the events in the DOM tree.

      Understanding Event Handling

      Events are an essential part of web development. They add interactivity to our applications and act as a bridge between the business logic and the user. Every DOM element can dispatch events. All we have to do is to subscribe for these events and process the received Event object.

      There is a term event propagation that stands behind event bubbling and event capturing both of which are two ways of event handling in DOM. Let’s use the following markup and see the difference between them.

      <div class="wrapper">
          <a href="#">click me</a>
      </div>

      We will attach click event handlers to the both elements. However, because there are nested into each other, they both will receive the click event.

      document.querySelector('.wrapper').addEventListener('click', function(e) {
          console.log('.wrapper clicked');
      });
      document.querySelector('a').addEventListener('click', function(e) {
          console.log('a clicked');
      });

      Once we press the link we see the following output in the console:

      a clicked
      .wrapper clicked

      So, indeed the both elements receive the click event. First, the link and then the <div>. This is the bubbling effect. From the deepest possible element to its parents. There is a way to stop the bubbling. Every handler receives an event object that has stopPropagation method:

      document.querySelector('a').addEventListener('click', function(e) {
          e.stopPropagation();
          console.log('a clicked');
      });

      By using stopPropagation function, we indicate that the event should not be sent to the parents.

      Sometimes we may need to reverse the order and have the event caught by the outer element. To achieve this, we have to use a third parameter in addEventListener. If we pass true as a value we will do event capturing. For example:

      document.querySelector('.wrapper').addEventListener('click', function(e) {
          console.log('.wrapper clicked');
      }, true);
      document.querySelector('a').addEventListener('click', function(e) {
          console.log('a clicked');
      }, true);

      That is how our browser process the events when we interact with the page.

      The Solution

      Okay, so why did we spend a section of the article talking about bubbling and capturing We mentioned them because bubbling is the answer of our problems with the popup. We should set the event listeners not to the links but to the <div> directly.

      var content = 'Are you sure?<br />';
      content += '<a href="#" class="popup--yes">Yes</a>';
      content += '<a href="#" class="popup--no">No</a>';
      
      var addListeners = function() {
          popup.addEventListener('click', function(e) {
              var link = e.target;
          });
      }
      
      showMessage(content);
      addListeners();

      By following this approach, we eliminate the issues listed in the beginning.

      • There is only one event listener and we are adding it once. No matter what we put inside the popup, the catching of the events will happen in their parent.
      • We are not bound to the additional content. In other words, we do not care when the showMessage is called. As long as the popup variable is alive we will catch the events.
      • Because we call addListeners once, we use the popup variable also once. We do not have to keep it or pass it between the methods.
      • Our code became flexible because we opted not care about the HTML passed to showMessage. We have access to the clicked anchor in that e.target points to the pressed element.

      The above code is better than the one that we started with. However, still doesn’t function the same way. As we said, e.target points to the clicked <a> tag. So, we will use that to distinguish the Yes and No buttons.

      var addListeners = function(callbacks) {
          popup.addEventListener('click', function(e) {
              var link = e.target;
              var buttonType = link.getAttribute('class');
              if(callbacks[buttonType]) {
                  callbacks[buttonType](e);
              }
          });
      }
      ...
      addListeners({
          'popup--yes': function() {
              console.log('Yes');
          },
          'popup--no': function() {
              console.log('No');
          }
      });

      We fetched the value of the class attribute and use it as a key. The different classes point to different callbacks.

      However, it is not a good idea to use the class attribute. It is reserved for applying visual styles to the element, and its value may change at any time. As JavaScript developers, we should use data attributes.

      var content = 'Are you sure?<br />';
      content += '<a href="#" data-action="yes" class="popup--yes">Yes</a>';
      content += '<a href="#" data-action="no" class="popup--no">No</a>';

      Our code becomes a little bit better too. We can remove the quotes used in addListeners function:

      addListeners({
          yes: function() {
              console.log('Yes');
          },
          no: function() {
              console.log('No');
          }
      });

      The result could be seen in this JSBin.

      Bubble.js

      I applied the solution above in several projects so it made sense to wrap it a library. It’s called Bubble.js and it is available in GitHub. It is 1.6K file that does exactly what we did above.

      Let’s transform our popup example to use Bubble.js. The first thing that we have to change is the used markup:

      var content = 'Are you sure?<br />';
      content += '<a href="#" data-bubble-action="yes" class="popup--yes">Yes</a>';
      content += '<a href="#" data-bubble-action="no" class="popup--no">No</a>';

      Instead of data-action we should use data-bubble-action.

      Once we include bubble.min.js in our page, we have a global bubble function available. It accepts a DOM element selector and returns the library’s API. The on method is the one that adds the listeners:

      bubble('.popup')
      .on('yes', function() {
          console.log('Yes');
      })
      .on('no', function() {
          console.log('No');
      });

      There is also an alternative syntax:

      bubble('.popup').on({
          yes: function() {
              console.log('Yes');
          },
          no: function() {
              console.log('No');
          }
      });

      By default, Bubble.js listens for click events, but there is an option to change that. Let’s add an input field and listens for its keyup event:

      <input type="text" data-bubble-action="keyup:input"/>

      The JavaScript handler still receives the Event object. So, in this case we are able to show the text of the field:

      bubble('.popup').on({
          ...
          input: function(e) {
              console.log('New value: ' + e.target.value);
          }
      });

      Sometimes we need to catch not one but many events dispatched by the same element. data-bubble-action accepts multiple values separated by comma:

      <input type="text" data-bubble-action="keyup:input, blur:inputBlurred"/>

      Find the final variant in a JSBin here.

      Fallbacks

      The solution provided in this article relies completely on the event bubbling. In some cases e.target may not point to the element that we need. 

      For example:

      <div class="wrapper">
          <a href="#">Please, <span>choose</span> me!</a>
      </div>
      

      If we place our mouse over "choose" and perform a click, the element that dispatches the event is not the <a> tag but the span element.

      Summary

      Admittedly, communication with the DOM is an essential part of our application development, but it is a common practice that we use frameworks just to bypass that communication. 

      We do not like adding listeners again and again. We do not like debugging weird double-event-firing bugs. The truth is that if we know how the browser works, we are able to eliminate these problems.

      Bubble.js is but one result of few hours reading and one hour coding - it's our 1.6K solution to one of the most common problems.

       

      0 Comments

      Leave a comment › Posted in: Daily

  • Page 3 of 26 pages  < 1 2 3 4 5 >  Last ›
  • Browse the Blog

    Syndicate

    governing-bruise