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

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

02
.
10
.
2024
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

HR Wave - No Bullshit HR Conference 2019

14
.
11
.
2023
Alicja Gruszczyk
HR
Conferences

Lightning Talks in your company

14
.
11
.
2023
Jarosław Kowalewski
Ruby on Rails
Visuality

Stress in Project Management

02
.
10
.
2024
Wiktor De Witte
HR
Project Management

How to find good developers and keep them happy - Part 1

02
.
10
.
2024
Michał Krochecki
HR
Visuality

PKP Intercity - Redesign and case study of polish national carrier

14
.
11
.
2023
Katarzyna Szewc
Design
Business
Frontend

Let’s prepare for GITEX Dubai together!

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

Ruby Quirks

14
.
11
.
2023
Jan Matusz
Ruby on Rails
Ruby

Visuality recognized as one of the Best Ruby on Rails Devs

14
.
11
.
2023
Maciej Zdunek
Ruby on Rails
Visuality
Business

Is the culture of the organization important?

14
.
11
.
2023
Alicja Gruszczyk
Conferences
Visuality

Between the devil and the deep blue sea

04
.
12
.
2023
Mateusz Wodyk
Project Management
Backend
HR

Let’s prototype!

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

5 marketing hacks which will make your life easier

14
.
11
.
2023
Maciej Zdunek
Marketing
Design