📜 ⬆️ ⬇️

Some interesting and useful things from a web developer *

* I hope ilusha_sergeevich will not accuse me of plagiarism.
If anything, post rename.

[There was a picture to attract attention]


')
Hello! Over the years, work gradually accumulates developments that could be shared with the community. But none of these developments draws on the big full post. Therefore, I collected all the little things that I remembered in one article: several simple open-sorts of projects, a couple of tips and findings. Each of the proposed scripts in this article is delivered as is, under the WTFPL license (except for the Balalaika). I will gladly accept pull requests with bug fixes or changes in the README.


donut.js - microbibrary drawing bureau (donut) and pie charts


image
While working on the next project, the task appeared to draw many informative bagels on the world map, and not just to draw, but also to support IE8, which, as we know, does not know how to SVG, but only ugly VML. The first thing that comes to mind is Raphael. Rummaging for a while, I found this solution . Unfortunately, the author showed ingenuity with a simple hack: he drew a white circle on a pie chart. This decision did not fit, as the donut hole should be transparent. Studying the possibility of drawing such diagrams with Raphael seemed to me too time-consuming. The rest of the scripts on the Internet, I also did not fit. I had to write my crutch, based on the mathematics of drawing arches of this project . The arches for the VML version are drawn using the arc element.
var myDonutDiv = donut(options); 


The donut function returns a div with a donut class containing a bagel in the form of SVG or VML. List of options:

Example:
 var myDonut = donut({ el: document.getElementById( 'container' ), size: 150, weight: 30, data: [{ value: 1, name: 'A' },{ value: 2, name: 'B', customData: 'Yeah' //    },{ value: 3, name: 'C' },{ value: 4, name: 'D' }], colors: [ '#80a8cc', '#da3b3e', '#ffa921', 'red' ] }); 


If the thickness of the arch is equal to half the diameter of the donut, a pie chart is obtained:
image

The donut function has two methods:
setColor(arc, color) - set the color for the arch
data(arc[, data]) - get or set the data for the arc

The arc argument is the DOM node ( path for SVG and arc for VML), which can be obtained by referring to the element with the [data-name=""] selector, where is the value of the name from the data.
 B_Arc = document.getElementById( 'container' ).querySelector( '[data-name="B"]' ) donut.data(B_Arc).customData; //  ,   ,  donut.setColor(B_Arc, '#8dc700'); //   


To add text, simply insert a text node in the returned block:
 text = myDonut.appendChild( document.createElement('span') ); text.className = 'donut-text'; text.innerHTML = '3'; 


 .donut-text { position: absolute; left: 0; line-height: 150px; //   width: 150px; //   text-align: center; font-size: 70px; } 


Position can be set in CSS or using the style property.

The solution works in the eighth donkey. Obsolete donkeys have not been verified, but I have no reason to believe that they will not work. The script does not depend on the availability of other libraries. Convenient implementation of the insertion of text, tooltips, etc. leave for you.

Living example
Repository


Balalaika


Balalaika is a tiny jQuery-like DOM library, narrowed to the limit, having a small but sufficient set of vanilla.js-pasan methods. It is based on the idea that in order to “get an item by ID”, you do not need to connect jQuery. The tiny size allows you to embed it anywhere.

For example, instead of connecting jQuery, as the main library:
 <script> $=_ </script> 

Full code
 <script> $=(function(n,e,k,h,p,m,l,b,d,g,f,c){c=function(a,b){return new ci(a,b)};ci=function(a,d){k.push.apply(this,a?a.nodeType||a==n?[a]:""+a===a?/</.test(a)?((b=e.createElement(d||"div")).innerHTML=a,b.children):(d&&c(d)[0]||e).querySelectorAll(a):/f/.test(typeof a)?/c/.test(e.readyState)?a():c(e).on("DOMContentLoaded",a):a:k)};ci[f="prototype"]=(c.extend=function(a){g=arguments;for(b=1;b<g.length;b++)if(f=g[b])for(d in f)a[d]=f[d];return a})(c.fn=c[f]=k,{on:function(a,d){a=a.split(h);this.map(function(c){(h[b= a[0]+(cb$=cb$||++p)]=h[b]||[]).push([d,a[1]]);c["add"+m](a[0],d)});return this},off:function(a,c){a=a.split(h);f="remove"+m;this.map(function(e){if(b=(g=h[a[0]+eb$])&&g.length)for(;d=g[--b];)c&&c!=d[0]||a[1]&&a[1]!=d[1]||(e[f](a[0],d[0]),g.splice(b,1));else!a[1]&&e[f](a[0],c)});return this},is:function(a){b=this[0];d=!!b&&(b.matches||b["webkit"+l]||b["moz"+l]||b["ms"+l]);return!!d&&d.call(b,a)}});return c})(window,document,[],/\.(.+)/,0,"EventListener","MatchesSelector"); </script> 


(similar to inserting a counter, just a little more massive)

As you know, the usual GET request takes time and considerable traffic. Inline insertion will save precious milliseconds before full page load.

Or as a local variable for your script:
 (function($) { //... })(_) 

Full code
 function($) { // your code starts here $(function() { $('.my-selector').on('click', function() { alert('I need my balalaika'); }); }); // your code ends here })((function(n,e,k,h,p,m,l,b,d,g,f,c){c=function(a,b){return new ci(a,b)};ci=function(a,d){k.push.apply(this,a?a.nodeType||a==n?[a]:""+a===a?/</.test(a)?((b=e.createElement(d||"div")).innerHTML=a,b.children):(d&&c(d)[0]||e).querySelectorAll(a):/f/.test(typeof a)?/c/.test(e.readyState)?a():c(e).on("DOMContentLoaded",a):a:k)};ci[f="prototype"]=(c.extend=function(a){g=arguments;for(b=1;b<g.length;b++)if(f=g[b])for(d in f)a[d]=f[d];return a})(c.fn=c[f]=k,{on:function(a,d){a=a.split(h);this.map(function(c){(h[b= a[0]+(cb$=cb$||++p)]=h[b]||[]).push([d,a[1]]);c["add"+m](a[0],d)});return this},off:function(a,c){a=a.split(h);f="remove"+m;this.map(function(e){if(b=(g=h[a[0]+eb$])&&g.length)for(;d=g[--b];)c&&c!=d[0]||a[1]&&a[1]!=d[1]||(e[f](a[0],d[0]),g.splice(b,1));else!a[1]&&e[f](a[0],c)});return this},is:function(a){b=this[0];d=!!b&&(b.matches||b["webkit"+l]||b["moz"+l]||b["ms"+l]);return!!d&&d.call(b,a)}});return c})(window,document,[],/\.(.+)/,0,"EventListener","MatchesSelector")); 



Since the Balalaika is inherited from the array, the developer has at his disposal all the methods of the arrays:


In addition, the Balalaika has jQuery-like methods on , off , is , extend and the document load event:
 $('.my-selector').on('click.namespace', function() { alert('I need my balalaika'); }); $('.my-selector').off('click.namespace'); $('.my-selector').on('click', function(evt) { if($(evt.target).is('.another-selector')) { alert('I need my balalaika'); } }); var myObject = {a:1}; $.extend(myObject,{ b: 2 }); $(function() { // Do something with DOM }); 


Such, at first glance, a meager set of methods allows you to do a lot. And because of the simplicity of the code and the vanilla functions, the balalaika greatly benefits the massive libraries in performance.

A few examples

Parsing:
 var elements = $('<div><span class="yeah"></span></div>'); 

Search for one item in another:
 var myElement = $('.my-selector', node); 

Style setting:
 $('.my-selector').forEach(function(el) { $.extend( el.style, { width: '30px', backgroundColor: 'red' }); }); 

Event Delegation:
 $('.my-selector').on('click', function(evt) { var node = evt.target; while(node !== this) { if($(node).is('.delegated-selector')) { // Handle it! break; } node = node.parentNode; } }); 

Simple plugin:
 $.fn.addClass = function( className ) { this.forEach( function( item ) { var classList = item.classList; classList.add.apply( classList, className.split( /\s/ ) ); }); return this; }; 

I do not propose to abandon jQuery, just do not forget that there is a micro-library with a funny name. Balalaika is used in the Matryoshka framework ( articles about the Matryoshka )

The library is supported by all browsers, starting with IE9.
Repository link


procrastinate function


Let me idly quote myself .
Imagine the following situation (taken from my practice). You have a form with certain text fields: checkboshes, etc. When the value of one of the form elements changes, the application must send a request to the server, which, in turn, returns data for rendering three graphs. Drawing graphs is hard for a processor and takes half a second on a weak computer ( Highcharts is that). Now imagine a user who is bored and he decided to repeatedly click on the checkbox. What will happen? A lot of requests will be sent, a bunch of answers will come back, which will also draw the schedule many times. What do they usually do in this case? Cancel the request to the server. The question is: why was this request to send, if you could wait until it calms down? :)

To solve this problem, I used the simplest function (possibly a bicycle), which takes another function as an argument and returns its modification, which can only be run once in a certain period of time. Example:
 var doSomethingHeavy = function( i ) { console.log( 'Ok', i ); }; var procrastinateSomethingHeavy = procrastinate( doSomethingHeavy ); for( var i = 0; i < 100; i++ ) { procrastinateSomethingHeavy( i ); } // >> Ok 100 

Function code
 var procrastinate = function ( f, d, thisArg ) { var timeout; if( typeof d !== 'number' ) { thisArg = d; d = 0; } return function() { var args = arguments, _this = this; clearTimeout( timeout ); timeout = setTimeout( function() { f.apply( thisArg || _this, args ); }, d || 0 ); }; }; 

The method, in addition to the "procraticizing" function, takes a delay, and the context as arguments. The delay is responsible for how many milliseconds the actual function call will be delayed during the next attempt to call it.

And here is an example of the case when the function will never be called (for better understanding).
 var procrastinateSomethingHeavy = MK.procrastinate( function() { console.log( 'Ok' ); }, 1000 ); setInterval( function() { procrastinateSomethingHeavy(); }, 500 ); //    

link to gist

State indeterminate checkbox


image
Did you know that the checkbox has an “average” state? I did not know. Yes, it does! It is set exclusively using JavaScript:
 checkbox.indeterminate = true; 

And you can access the checkbox from CSS with this state using the pseudo-class: indeterminate.

More opsevdlassey
Article in English (even if you do not know the language, the code speaks for itself)


Nasty malware


In search of a specific release of the Internet show This is Good on the site, posing as an official (or indeed official but hacked), I came across the page “MIA of Ukraine”, where I was accused of watching porn with youngsters and other nasty things. A non-closing window appeared on the page with the text saying that he had to pay a fine by sending an SMS to a specific number. I tried to close the page anyway, it did not work ... As a result, I shut down the network on the laptop and reloaded Chrome several times to clear the hash. Naturally, I was curious how the script managed to pull the nerves of an experienced web developer, and I opened this page in a wonderful browser called links (if anything, this is a console browser).

The malicious code was:
 onbeforeunload=function(){ location.reload(); return "     Ń– . Ń–Ń–Ń—   'Ń– . Ń– Ń–  Ń–.  12  Ń–     . ,    Ń” 12 ,   , -  ,    ' Ń–Ń— ." }; onload=function(){ location.reload(); } 

The page tries to reload, and here the onbeforeunload handler is onbeforeunload , which also tries to reload the page, causing endless stupid text to appear.

How to try malware in your browser? Create a local HTML file with the following content:
 <html> <head> <title></title> <meta charset="utf-8"> <style></style> </head> <body> <script> onbeforeunload=function(){ location.reload(); return " " }; onload=function(){ location.reload(); } </script> </body> </html> 

Open the page in Chrome (in Firefox the malware is not so malicious) and enjoy. When you get bored, delete or rename the file.


Idea: mousedown instead of click


UPD: You need to be careful with this approach. More in the comments.
Want to subjectively increase the speed of the interface? Replace the click event with mousedown , if possible (if there are no drag n drops or double click events). This will avoid delays in the tenths or hundredths of a second before the user releases the mouse button. It will seem to the user of your application that the interface works faster, since the code will work a little faster than it is instantly (excluding heavy operations and requests to the server).

A small demo.


Tip: use less CDN


A library request is a complex HTTP request. Developers with experience know this and put all JS code in one file using Grunt, r.js or another collector. This will save time on unnecessary requests and, often, traffic, since GET even with the answer 304 weighs a lot. In addition, the CDN may not respond. Programmers often solve this with failback, but this is a bad option, because the browser does not immediately understand that the server is down, and the user will have to enjoy the loading page for a few precious seconds.


SuitUp editor and WYSIWYG debris removal


There is such an editor, written by me, which is made in the form of a jQuery plugin, called SuitUp . The biggest drawback of the editor is the lack of cleaning the resulting HTML from the inserted formatted text, for example, from Like: from styles, spans, service comments and other nonsense.

I present a small function (also jQuery plugin, but with vanilla content), which using the DOM methods, without regulars, recursively cleans any contenteditable block from comments, from XML, SCRIPT, STYLE, LINK, META tags, from style and align attributes. The Cleaner was created a week ago, therefore, bugs and unrecorded cases of dirty HTML are not excluded.

 jQuery( editor ).on( 'paste', function( evt ) { setTimeout( function() { jQuery( this ).mswordFilter(); }.bind( this ) ); }); 

Gist Link


The simplest polyfill for addEventListener


The eighth donkey, as you know, does not know how to addEventListener method, it has a non- attachEvent . For programmers who do not need custom events and other bells and whistles, I suggest my own small polyfill used in the Matryoshka.

Link to AMD version
Link to the regular version


Report about typos on the site


There is such a wonderful system of notifications about typos on the site, called Orphus . The user, finding a typo or inaccuracy can select the appropriate text and press Ctrl + Enter. A window opens in which the user can enter a comment to the error, and the developer, then logging in on the Orfus website, sees all the alerts and, if he wants, corrects the errors. I wanted to write my typo notification for the Matryoshka documentation page. As a basis, I took the well-known hack of sending a form to a google form page, which does not require the developer to raise his own server.

How to connect?
1. Create a Google-form with three fields: text with an error, user comment, link to the page. An example .
2. Using the web inspector, examine the form. We need the value of the attribute of the form action and the names of the inputs (name attribute)
3. Connect the script before
4. Run the function:
 typo({ formURL: FORM_ACTION_URL, selectionName: NAME_OF_SELECTION_INPUT, commentName: NAME_OF_COMMENT_INPUT, pageName: NAME_OF_URL_INPUT }); 


For example:
 typo({ formURL: 'https://docs.google.com/forms/d/1sQhv81wN65__7H4quwhDbecvtUxzAGZ-lMmlwF9MKcc/formResponse', selectionName: 'entry.1972481987', commentName: 'entry.1777335671', pageName: 'entry.339184258' }); 

Repository
Live demo
"Typos" from the demo will appear in this table .

Ideally, the script needs to wait for the readiness of the DOM and the custom window (they do not reach the hand itself). If someone does this, drop the link, happy to fork :)
The script does not depend on the presence of third-party libraries.


vanillatree - Vanilla Jstree Substitute


image
jstree - jQuery plugin that creates a nested list tree. This plugin did not suit me with a complicated, cumbersome API. vanillatree is a compact jstree replacement that does not require the connection of any third-party library. The list of features includes: a custom context menu for each branch, the choice of a branch to open or close a tree under a click-through branch (or using methods), moving a branch, deleting a branch and the corresponding events.

How to use?

Connect the appropriate JS and CSS file and, when the DOM tree is ready, create an instance of VanillaTree
 var tree = new VanillaTree(treeElement, options); 

The first argument must be an element or element selector, the second is optional options:
placeholder (String) - shown when the tree is empty
contextmenu (Array) - a list of objects responsible for the context menu ( {label: , action: } )

 var tree = new VanillaTree('.my-selector', { placeholder: 'None of leafs is added yet', contextmenu: [{ label: 'Label 1', action: function(id) { // someAction } },{ label: 'Label 2', action: function(id) { // someAction } }] }); 


After initialization, you can add leaves to the tree.
 tree.add({ label: 'Label A', //   id: 'a', // ID , ,   ,   parent: 'b', // ID  () opened: true, //      () selected: true //      () }); 


Here is a complete list of methods:


VanillaTree triggers custom events.


The vanilla tree uses the dispatchEvent method and the CustomEvent constructor, if possible, to generate custom events. The ID of the branch that triggered the event is contained in the evt.detail object. All events are pop-up, i.e. you can listen to events of a particular branch in the parent, parent of the parent, ... and so on, before the document.
 treeElement.addEventListener('vtree-open', function(evt) { info.innerHTML = evt.detail.id + ' is opened'; }); treeElement.addEventListener('vtree-close', function(evt) { info.innerHTML = evt.detail.id + ' is closed'; }); treeElement.addEventListener('vtree-select', function(evt) { info.innerHTML = evt.detail.id + ' is selected'; }); //... 


Please note that vanillatree uses the Balalaika as a “local library”. Take a look at line 197 .

The script works in all modern browsers, including IE10 (the day before I replaced the dataset with getAttribute / setAttribute , and CustomEvent with initEvent ). For IE9 you need a classList classList .

Link to a live example
Repository link

All good!

Source: https://habr.com/ru/post/234279/


All Articles