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

A look back at Friendly.rb 2023

14
.
11
.
2023
Cezary Kłos
Conferences
Ruby

Debugging Rails - Ruby Junior Chronicles

14
.
11
.
2023
Piotr Witek
Ruby on Rails
Backend
Tutorial

GraphQL in Ruby on Rails: How to Extend Connections

14
.
11
.
2023
Cezary Kłos
Ruby on Rails
GraphQL
Backend
Tutorial

Tetris on Rails

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

EURUKO 2023 - here's what you've missed

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

Easy introduction to Connection Pool in ruby

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

When crazy ideas bring great time or how we organized our first Conference!

04
.
12
.
2023
Alexander Repnikov
Ruby on Rails
Conferences
Visuality

Stacey Matrix & Takeaways - why does your IT project suck?

02
.
10
.
2024
Wiktor De Witte
Project Management
Business

A simple guide to pessimistic locking in Rails

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

Poltrax design - story of POLTRAX (part 3)

04
.
12
.
2023
Mateusz Wodyk
Startups
Business
Design

Writing Chrome Extensions Is (probably) Easier Than You Think

14
.
11
.
2023
Antoni Smoliński
Tutorial
Frontend
Backend

Bounded Context - DDD in Ruby on Rails

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

The origin of Poltrax development - story of POLTRAX (part 2)

29
.
11
.
2023
Stanisław Zawadzki
Ruby on Rails
Startups
Business
Backend

Ruby Meetups in 2022 - Summary

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

Repository - DDD in Ruby on Rails

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

Example Application - DDD in Ruby on Rails

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

How to launch a successful startup - story of POLTRAX (part 1)

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

How to use different git emails for different projects

14
.
11
.
2023
Michał Łęcicki
Backend
Tutorial

Aggregate - DDD in Ruby on Rails

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

Visuality at wroc_love.rb 2022: It's back and it's good!

14
.
11
.
2023
Patryk Ptasiński
Ruby on Rails
Conferences
Ruby

Our journey to Event Storming

14
.
11
.
2023
Michał Łęcicki
Visuality
Event Storming

Should I use Active Record Callbacks?

14
.
11
.
2023
Mateusz Woźniczka
Ruby on Rails
Backend
Tutorial

How to rescue a transaction to roll back changes?

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