14
.
11
.
2023
21
.
07
.
2015
Frontend

Use less javascript plugins

Michał Młoźniak
Co-founder

This won't be any deep-down post about general use of javascript libraries as title might suggest. I just want to show one particular problem that I had and how I found solution, so hopefully it will help someone else.

Going straight to the issue. One of our projects is about booking services like haircut or swedish massage, whatever that is. On the other side there is merchant that can specify in what time his employees work. Merchant can set start and finish hour per day. To make this work, we are using timepicker plugin and inputmask plugin. Timepicker generates dropdown with possible hours, but merchant can also input custom hour and inputmask is used to valid that input.

Last week we got a bug report that this timepicker is not working in Safari browser. After clicking on particular hour dropdown was disappearing but the value was not set.

First try: jquery-timepicker plugin

So my first guess was that there is a bug in timepicker. I've updated it to the newest version, but it didn't solved a problem. I've started looking into the source code, near line where value was set and added some console logs there:

function _setTimeValue(self, value, source)
{
  if (self.is('input')) {
    self.val(value);
    console.log('value to set', value);
    console.log('actual value', self.val());

    var settings = self.data('timepicker-settings');
    if (settings.useSelect && source != 'select' && source != 'initial') {
      self.data('timepicker-list').val(_roundAndFormatTime(_time2int(value), settings));
    }
  }

  // code omitted for brevity
}

So I was expecting to see self.val() to be equal value, but it was still the previous value for that field. The problem was not in jquery-timepicker.

Second try: inputmask plugin

So another guess was that there must be something wrong with inputmask. Temporarily commenting it out fixed my problem. I've started digging into the source code and found function that is used to override getter and setter for provided field. This function is pretty complicated, handling a lot of cases for different browsers. In case of Safari, PatchValhook function was called.

function patchValueProperty(npt) {
  var valueGet;
  var valueSet;

  function PatchValhook(type) {
    if ($.valHooks[type] == undefined || $.valHooks[type].inputmaskpatch != true) {
      var valhookGet = $.valHooks[type] && $.valHooks[type].get ? $.valHooks[type].get : function(elem) {
        return elem.value;
      };
      var valhookSet = $.valHooks[type] && $.valHooks[type].set ? $.valHooks[type].set : function(elem, value) {
        elem.value = value;
        return elem;
      };

      $.valHooks[type] = {
        get: function(elem) {
          var $elem = $(elem);
          if (elem.inputmask) {
            if (elem.inputmask.opts.autoUnmask)
              return elem.inputmask.unmaskedvalue();
            else {
              var result = valhookGet(elem),
                maskset = elem.inputmask.maskset,
                bufferTemplate = maskset['_buffer'];
              bufferTemplate = bufferTemplate ? bufferTemplate.join('') : '';
              return result != bufferTemplate ? result : '';
            }
          } else return valhookGet(elem);
        },
        set: function(elem, value) {
          var $elem = $(elem),
            result;
          result = valhookSet(elem, value);
          if (elem.inputmask)
            $elem.triggerHandler('setvalue.inputmask');
          return result;
        },
        inputmaskpatch: true
      };
    }
  }

  // code omitted for brevity
}

Important line is this one:

result = valhookSet(elem, value);

It should set the new value, but it wasn't working. I've added some console logs to see what was field value after calling this function, but it didn't change. Very strange. This line is just calling valhookSet defined above:

var valhookSet = $.valHooks[type] && $.valHooks[type].set ? $.valHooks[type].set : function(elem, value) {
  elem.value = value;
  return elem;
};

This is trivial function, doesn't look like there is something wrong with it. I've copied function body inline instead of calling it and it worked. So it seems that valhookSet was something else. I've displayed it source code to console and there was this code:

// Get the element, and its data.
var $this = $(el),
  data  = $this.data('numFormat');

// Does this element have our data field?
if( !data )
{

    // Check if the valhook function already exists
    if( $.isFunction( origHookSet ) )
    {
        // There was, so go ahead and call it
        return origHookSet(el,val);
    }
    else
    {
        // No previous function, return undefined to have jQuery
        // take care of retrieving the value
        return undefined;
  }
}
else
{
  if(val == '')
  {
    return el.value = '';
  }
  // Otherwise, don't worry about other valhooks, just run ours.
  return el.value = $.number( val, data.decimals, data.dec_point, data.thousands_sep );
}

The culprit: jquery-number plugin

So it turns out this code is from jquery-number plugin. It is defining its own valhooks for reading numerical values from fields and it was conflicting with inputmask plugin.

Since we didn't use jquery-number plugin much, I've just removed it. I've only copied one function for formating numeric values.

In summary you should pay attention at what you add to your project and do you really need adding whole library if you only need couple of functions. It is always better to have less, there is less chance that there will be some problems with conflicting libraries and overall javascript will be smaller.

Michał Młoźniak
Co-founder

Check my Twitter

Check my Linkedin

Did you like it? 

Sign up To VIsuality newsletter

READ ALSO

Value Object - DDD in Ruby on Rails

17
.
03
.
2024
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

Introduction to DDD in Ruby on Rails

17
.
03
.
2024
Paweł Strzałkowski
Ruby on Rails
Domain-Driven Design
Backend
Tutorial

Safe data migrations in Rails

17
.
03
.
2024
Paweł Strzałkowski
Ruby on Rails
Backend
Tutorial

I love dev, and so do we!

14
.
11
.
2023
Michał Łęcicki
Software
Conferences

Updated guide to recruitment process at Visuality

14
.
11
.
2023
Michał Łęcicki
Visuality
HR

Visuality Academy for wannabe Junior Engineers

14
.
11
.
2023
Michał Piórkowski
HR
Visuality

How to approach funding as an MVP

14
.
11
.
2023
Michał Piórkowski
Business
Startups

Visuality 13th birthday

14
.
11
.
2023
Michał Piórkowski
HR
Visuality

How To Receive Emails With a Rails App in 2021

14
.
11
.
2023
Michał Łęcicki
Ruby on Rails
Tutorial

Project Quality in IT - How to Make Sure You Will Get What You Want?

14
.
11
.
2023
Wiktor De Witte
Ruby on Rails
Project Management
Business

5 Trends in HR Tech For 2021

14
.
11
.
2023
Maciej Zdunek
Business
Project Management

Is Go Language the Right Choice for Your Next Project?

14
.
11
.
2023
Maciej Zdunek
Backend
Business

SXSW Tradeshow 2020: Get Your FREE Tickets and Meet Us

14
.
11
.
2023
Michał Krochecki
Ruby on Rails
Conferences
Frontend
Backend
Business

How to build effective website: simplicity & McDonald's

14
.
11
.
2023
Lukasz Jackiewicz
Ruby on Rails
Frontend
Design

Thermal Printer Protocols for Image and Text

14
.
11
.
2023
Burak Aybar
Backend
Tutorial
Software

WebUSB - Print Image and Text in Thermal Printers

14
.
11
.
2023
Burak Aybar
Backend
Tutorial
Software

What happened in Visuality in 2019

14
.
11
.
2023
Maciej Zdunek
Visuality
HR

Three strategies that work in board games and in real life

14
.
11
.
2023
Michał Łęcicki
Ruby on Rails