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

WebUSB - Bridge between USB devices and web browsers

14
.
11
.
2023
Burak Aybar
Ruby on Rails
Frontend
Backend
Tutorial

Visuality comes to town - this time it's Poznań

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

CSS Modules in Rails

14
.
11
.
2023
Adam Król
Ruby on Rails
Tutorial
Backend
Frontend

How to choose a software house.

14
.
11
.
2023
Michał Piórkowski
Ruby on Rails
Business
Visuality

JSON API versus the NIH syndrome

14
.
11
.
2023
Nadia Miętkiewicz
Backend
Frontend
Tutorial

From Idea to Concept

02
.
10
.
2024
Michał Krochecki
Ruby on Rails
Business
Startups

Styling React Components

14
.
11
.
2023
Umit Naimian
Ruby on Rails
Frontend
Tutorial

How good design can help your business grow

14
.
11
.
2023
Lukasz Jackiewicz
Design
Business
Marketing

TODO not. Do, or do not.

29
.
11
.
2023
Stanisław Zawadzki
Ruby on Rails
Software

CS Lessons #003: Density map in three ways

14
.
11
.
2023
Michał Młoźniak
Ruby
Backend
Tutorial
Software

Clean code for the win

14
.
11
.
2023
Michał Piórkowski
Ruby on Rails
Backend
Frontend
Business

Crowd-operated Christmas Lights

14
.
11
.
2023
Nadia Miętkiewicz
Ruby on Rails
Backend

How to startup and be mature about it

14
.
11
.
2023
Rafał Maliszewski
Ruby on Rails
Startups
Business

A journey of a thousand miles begins with workshops

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

CS Lessons #002: Data structures

14
.
11
.
2023
Michał Młoźniak
Ruby
Software

Summary of Phoenix workshop at Visuality

14
.
11
.
2023
Karol Słuszniak
Ruby on Rails
Visuality
Backend

CS Lessons #000: Introduction and motivation

14
.
11
.
2023
Michał Młoźniak
Ruby
Software

CS Lessons #001: Working with binary files

14
.
11
.
2023
Michał Młoźniak
Ruby
Software

Working with 40-minute intervals

14
.
11
.
2023
Sakir Temel
Software
HR

THE MATURE TECH STARTUP DILEMMA: WHAT'S NEXT

14
.
11
.
2023
Susanna Romantsova
Startups

Win MVP workshop!

14
.
11
.
2023
Susanna Romantsova
Startups

FINTECH WEEK IN OSLO: WHATs & WHYs

14
.
11
.
2023
Susanna Romantsova
Conferences

MY FIRST MONTH AT VISUALITY

14
.
11
.
2023
Susanna Romantsova
Visuality
HR

NASA 1st global hackaton in Poland? Visuality Created it!

14
.
11
.
2023
Rafał Maliszewski
Ruby on Rails

Berlin StartupCamp 2016 summary

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