I found this code while doing a random task, and I'm wondering how it works with the memory in JS.
There are obviously more that one level of nested functions, which reference variables from all sorts of levels above (outer contexts). Specifically the variable i.
!function () {
function valOrDef(val, def) {
var _val, _def = typeof def === 'undefined' ? 0 : def;
if (typeof val !== 'undefined' && typeof val !== null) _val = parseFloat(val, 10);
return !isNaN(_val) ? _val : _def;
}
function reg(els,l) {
if (!els || !(l = els.length)) return;
function h(el) {
var p = el.querySelector('button.decrease'),
i = el.querySelector('input'),
n = el.querySelector('button.increase');
var min = valOrDef(i.min, 0), step = valOrDef(i.step, 1);
function inc(e) { e.preventDefault(); e.stopPropagation(); i.value = Math.max(min, valOrDef(i.value, 0) + step); fire(); }
function dec(e) { e.preventDefault(); e.stopPropagation(); i.value = Math.max(min, valOrDef(i.value, 0) - step); fire(); }
function chk() { i.value = valOrDef(i.value, min); }
function fire() { i.dispatchEvent(new Event('change')); }
p.addEventListener('pointerup', dec, true);
n.addEventListener('pointerup', inc, true);
i.addEventListener('change', chk, false);
}
while (l--) h(els[l]);
}
reg(document.querySelectorAll('.single-inc-dec'));
}();
So, I'm wondering, is that a wasting memory, because each callback has it's own "environment" with it's own pointers?
I think it will be more efficent to have the methods in an outer scope, and provide them with arguments. In this case the inc, dec & fire outside the reg function, and recieve the i as a parameter. So something like this:
!function () {
function valOrDef(val, def) {
var _val, _def = typeof def === 'undefined' ? 0 : def;
if (typeof val !== 'undefined' && typeof val !== null) _val = parseFloat(val, 10);
return !isNaN(_val) ? _val : _def;
}
function inc(i) { return function(e) { e.preventDefault(); e.stopPropagation(); i.value = Math.max(min, valOrDef(i.value, 0) + step); fire(i); } }
function dec(i) { return function(e) { e.preventDefault(); e.stopPropagation(); i.value = Math.max(min, valOrDef(i.value, 0) - step); fire(); }
function fire(i) { i.dispatchEvent(new Event('change')); }
function reg(els,l) {
if (!els || !(l = els.length)) return;
function h(el) {
var p = el.querySelector('button.decrease'),
i = el.querySelector('input'),
n = el.querySelector('button.increase');
var min = valOrDef(i.min, 0), step = valOrDef(i.step, 1);
function chk() { i.value = valOrDef(i.value, min); }
p.addEventListener('pointerup', dec(i), true);
n.addEventListener('pointerup', inc(i), true);
i.addEventListener('change', chk, false);
}
while (l--) h(els[l]);
}
reg(document.querySelectorAll('.single-inc-dec'));
}();
I know that this code will still produce a callback-function for each instance of the listeners, just wondering about the mechanics behind this.
Any pointers to what keywords I should be lookign for to read up on this are also welcomed. :)
Please don't crusify me over this code. It's not mine. I was just told "The buttons are slow on the iPhone. Fix that", while doing that I found this gem.
Related
I have solved the problem Count the smiley faces:
Given an array (arr) as an argument complete the function countSmileys that should return the total number of smiling faces.
Rules for a smiling face:
Each smiley face must contain a valid pair of eyes. Eyes can be marked as : or ;
A smiley face can have a nose but it does not have to. Valid characters for a nose are - or ~
Every smiling face must have a smiling mouth that should be marked with either ) or D
No additional characters are allowed except for those mentioned.
Valid smiley face examples: :) :D ;-D :~)
Invalid smiley faces: ;( :> :} :]
Example
countSmileys([':)', ';(', ';}', ':-D']); // should return 2;
countSmileys([';D', ':-(', ':-)', ';~)']); // should return 3;
countSmileys([';]', ':[', ';*', ':$', ';-D']); // should return 1;
Note
In case of an empty array return 0. You will not be tested with invalid input (input will always be an array). Order of the face (eyes, nose, mouth) elements will always be the same.
Then when I look through the solutions I find that many people use regexp. Then I want write a state machine to implement regexp and solve this problem. But I failed. This is my code:
function countSmileys(smileys) {
let state = smileyHasValidEye;
return smileys.filter(smiley => {
for (let s of [...smiley]) {
state = state(s);
}
return state === true;
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return;
}
console.log(countSmileys([':)', ';(', ';}', ':-D']));
And the error I get is:
state = state(s);
^
TypeError: state is not a function
Then I debugged my code I found the procedure doesn't enter the smileyHasValidNose function. Then I don't know the reason.
The problem is you don't really reset state in between smileys. So the next smiley state will be true which you can't call (it's not a function).
You could use a local variable for state that resets it to the first function (the first step).
function countSmileys(smileys) {
let firstStep = smileyHasValidEye;
return smileys.filter(smiley => {
let state = firstStep;
for (let s of [...smiley]) {
state = state(s);
}
return state === true;
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return;
}
console.log(countSmileys([':)', ';(', ';}', ':-D']));
This code however, will error if there's more on the string besides the smiley (or a partial of the smiley).
I would change smileyHasValidMouth to return false if it doesn't detect a smiley. Just to be more consistent here...
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return false;
}
And adjust your loop to exit early if it finds a value that is not a function.
for (let s of [...smiley]) {
state = state(s);
if(typeof state !== 'function') return state;
}
function countSmileys(smileys) {
let firstStep = smileyHasValidEye;
return smileys.filter(smiley => {
let state = firstStep;
for (let s of [...smiley]) {
state = state(s);
if (typeof state !== 'function') return state;
}
}).length;
}
function smileyHasValidEye(s) {
if (s === ':' || s === ';') {
return smileyHasValidNose;
}
return smileyHasValidEye;
}
function smileyHasValidNose(s) {
if (s === '-' || s === '~') {
return smileyHasValidMouth;
}
return smileyHasValidMouth(s);
}
function smileyHasValidMouth(s) {
if (s === ')' || s === 'D') {
return true;
}
return false;
}
console.log(countSmileys([':~(', ':>', ':D', ':(', ':o>', ';)', ':)']));
I have a section of code where a variable contains a particular string (here it's multiply), and when I check if the variable has that particular string, the condition always equates to false. I cannot find what I'm missing here.
// calculations
$scope.$watch('colInput.' + el.key, function () {
angular.forEach($scope.colInput[el.key], function (item, index) {
angular.forEach($scope.column[el.key], function (item_1, index_1) {
if (item.hasOwnProperty(item_1.key)) {
item[item_1.key].type = item_1.type;
item[item_1.key].id = item_1.id;
item[item_1.key].options = item_1.options;
}
else {
item[item_1.key] = {};
item[item_1.key].type = item_1.type;
item[item_1.key].id = item_1.id;
item[item_1.key].options = item_1.options;
}
})
angular.forEach(item, function (elem, key) { //each column of the row
var operand_1, operator, operand_2;
if (elem.type == 10) {
// analyzing the formula
elem.options.forEach(function (el, index) {
if (isNaN(el) && index == 1) {
operator = el;
} else if (isNaN(el) && index == 0) {
operand_1 = el;
} else if (isNaN(el) && index == 2) {
operand_2 = el;
} else if (!isNaN(el)) {
operand_2 = parseFloat(el);
}
})
console.log(operator, eval(operator === "multiply"), typeof operator);
if (operator == 'multiply') {
console.log("IF---")
elem.value = parseFloat(item[operand_1].value) * operand_2;
}
}
})
})
}, true)
It looks like your operator is an HTML element not a String.
The comparison with multiply will always be false.
Can someone please show me how to remove this div?
I tried everything I could find. Some of the things I've tried are:
jQuery(document).ready(function ($) {
if (seconds == 0 ) {
$(".meows").hide();
return;
}
});
this:
jQuery(document).ready(function ($) {
if (seconds == 0 ) {
$('.meows').css('display','none');
return;
}
});
this:
document.onreadystatechange = function () {
if (document.readyState == "complete") {
document.getElementsByClassName("meows")[0].style.display = "none";
}
}
this:
document.addEventListener('DOMContentLoaded', function() {
if (seconds == 0 ) {
document.getElementsByClassName("meows")[0].style.display = "none";
//document.getElementById('timer').innerHTML = 'EXPIRED!';
return;
}
});
and more. Most of these actually make the error go away. The error I get is:
TypeError: Cannot read property 'style' of undefined
What it is is a popup notification. It popups right under the body tag. So it's not there all the time.
Here is where the popup gets created:
if (typeof default_meow_area === 'undefined' && typeof options.container === 'undefined') {
default_meow_area = $(window.document.createElement('div'))
.attr({'id': ((new Date()).getTime()), 'class': 'meows'});
$('body').prepend(default_meow_area);
}
I'm not sure this is enough information to figure it out so here is the whole js file:
(function ($, window) {
'use strict';
// Meow queue
var default_meow_area,
meows = {
queue: {},
add: function (meow) {
this.queue[meow.timestamp] = meow;
},
get: function (timestamp) {
return this.queue[timestamp];
},
remove: function (timestamp) {
delete this.queue[timestamp];
},
size: function () {
var timestamp,
size = 0;
for (timestamp in this.queue) {
if (this.queue.hasOwnProperty(timestamp)) { size += 1; }
}
return size;
}
},
// Meow constructor
Meow = function (options) {
var that = this;
this.timestamp = new Date().getTime(); // used to identify this meow and timeout
this.hovered = false; // whether mouse is over or not
if (typeof default_meow_area === 'undefined'
&& typeof options.container === 'undefined') {
default_meow_area = $(window.document.createElement('div'))
.attr({'id': ((new Date()).getTime()), 'class': 'meows'});
$('body').prepend(default_meow_area);
}
if (meows.size() <= 0) {
if (typeof options.beforeCreateFirst === 'function') {
options.beforeCreateFirst.call(that);
}
}
if (typeof options.container === 'string') {
this.container = $(options.container);
} else {
this.container = default_meow_area;
}
if (typeof options.title === 'string') {
this.title = options.title;
}
if (typeof options.message === 'string') {
this.message = options.message;
} else if (options.message instanceof $) {
if (options.message.is('input,textarea,select')) {
this.message = options.message.val();
} else {
this.message = options.message.text();
}
if (typeof this.title === 'undefined' && typeof options.message.attr('title') === 'string') {
this.title = options.message.attr('title');
}
}
if (typeof options.icon === 'string') {
this.icon = options.icon;
}
if (options.sticky) {
this.duration = Infinity;
} else {
this.duration = options.duration || 7000;
}
// Call callback if it's defined (this = meow object)
if (typeof options.beforeCreate === 'function') {
options.beforeCreate.call(that);
}
// Add the meow to the meow area
this.container.append($(window.document.createElement('div'))
.attr('id', 'meow-' + this.timestamp.toString())
.addClass('meow')
.html($(window.document.createElement('div')).addClass('inner').html(this.message))
.hide()
.fadeIn('400', function() {
$('.meows').animate({'bottom':'0'},500);
}));
this.manifest = $('#meow-' + this.timestamp.toString());
// Add title if it's defined
if (typeof this.title === 'string') {
this.manifest.find('.inner').prepend(
$(window.document.createElement('p')).text(this.title)
);
}
// Add icon if it's defined
if (typeof that.icon === 'string') {
this.manifest.find('.inner').prepend(
$(window.document.createElement('div')).addClass('icon').html(
$(window.document.createElement('img')).attr('src', this.icon)
)
);
}
// Add close button if the meow isn't uncloseable
// TODO: this close button needs to be much prettier
if (options.closeable !== false) {
this.manifest.find('.inner').prepend(
$(window.document.createElement('a'))
.addClass('close')
.html('×')
.attr('href', '#close-meow-' + that.timestamp)
.click(function (e) {
e.preventDefault();
that.destroy();
})
);
}
this.manifest.bind('mouseenter mouseleave', function (event) {
if (event.type === 'mouseleave') {
that.hovered = false;
that.manifest.removeClass('hover');
// Destroy the mow on mouseleave if it's timed out
if (that.timestamp + that.duration <= new Date().getTime()) {
that.destroy();
}
} else {
that.hovered = true;
that.manifest.addClass('hover');
}
});
// Add a timeout if the duration isn't Infinity
if (this.duration !== Infinity) {
this.timeout = window.setTimeout(function () {
// Make sure this meow hasn't already been destroyed
if (typeof meows.get(that.timestamp) !== 'undefined') {
// Call callback if it's defined (this = meow DOM element)
if (typeof options.onTimeout === 'function') {
options.onTimeout.call(that.manifest);
}
// Don't destroy if user is hovering over meow
if (that.hovered !== true && typeof that === 'object') {
that.destroy();
}
}
}, that.duration);
}
this.destroy = function () {
if (that.destroyed !== true) {
// Call callback if it's defined (this = meow DOM element)
if (typeof options.beforeDestroy === 'function') {
options.beforeDestroy.call(that.manifest);
}
that.manifest.find('.inner').fadeTo(400, 0, function () {
that.manifest.slideUp(function () {
that.manifest.remove();
that.destroyed = true;
meows.remove(that.timestamp);
if (typeof options.afterDestroy === 'function') {
options.afterDestroy.call(null);
}
if (meows.size() <= 0) {
if (default_meow_area instanceof $) {
default_meow_area.remove();
default_meow_area = undefined;
}
if (typeof options.afterDestroyLast === 'function') {
options.afterDestroyLast.call(null);
}
}
});
});
}
};
};
$.fn.meow = function (args) {
var meow = new Meow(args);
meows.add(meow);
return meow;
};
$.meow = $.fn.meow;
}(jQuery, window));
/*! countdown timer I added below */
var timeoutHandle;
function countdown(minutes,stat) {
var seconds = 60;
var mins = minutes;
if(getCookie("minutes")&&getCookie("seconds")&&stat)
{
var seconds = getCookie("seconds");
var mins = getCookie("minutes");
}
function tick() {
var counter = document.getElementById("timer");
setCookie("minutes",mins,10)
setCookie("seconds",seconds,10)
var current_minutes = mins-1
seconds--;
counter.innerHTML = current_minutes.toString() + ":" + (seconds < 10 ? "0" : "") + String(seconds);
//save the time in cookie
if( seconds > 0 ) {
timeoutHandle=setTimeout(tick, 1000);
} else {
if(mins > 1){
setTimeout(function () { countdown(parseInt(mins)-1,false); }, 1000);
}
}
jQuery(document).ready(function () {
if (seconds == 0 ) {
document.getElementsByClassName("meows")[0].style.display = "none";
//document.getElementById('timer').innerHTML = 'EXPIRED!';
return;
}
)};
}
tick();
}
function setCookie(cname,cvalue,exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname+"="+cvalue+"; "+expires;
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
window.onload = function startingTimer(){
//countdown(prompt("Enter how many minutes you want to count down:"),true);
countdown(1,true);
}
Use event delegation to register events for all present and future elements that match your selector. In your case, you can use something like this
$('body').delegate('.meows', 'click', function() {
console.log(this);
});
The above delegation is for click event but you can bind any event to accomplish your needs.
Another method is to use MutationObserver. I won't go to it's details here because the above method can solve your problem.
I want to get keyCode if they are pressed with shift Key,
for ex. I press a and then b. Then I want to store it in one array like this [shiftkey,65,66] please suggest me if this is possible or I am going wrong.
You can try this sanjay:
<input id="down" type="text"/>
var your_array = [];
document.onkeydown = function (e) {
var keyPress;
if (typeof event !== 'undefined') {
keyPress = this.value + String.fromCharCode(e.keyCode);
}
else if (e) {
keyPress = e.which;
}
if(keyPress == '16'){
keyPress = 'shift';
}
your_array.push(keyPress);
alert(your_array);
// returns [shift,65,66];
return false; // Prevents the default action
};
var a = [];
var wait = true;
window.onkeydown = function (e) {
if (wait) {
if (a[0] != e.keyCode) { a[0] = e.keyCode; }
}
wait = false;
window.addEventListener('keyup', key_up, false);
}
function key_up(e) {
a[1] = e.keyCode;
if (a[0] == a[1]) { a = []; }
wait = true;
window.removeEventListener('keyup', key_up, false);
console.log(a);
alert(a);
}
var pressedKeysSeries = [];
$(document).on('keyup', function (e) { pressedKeysSeries = []; });
$(document).on('keydown', function (e) {
if (e.shiftKey && pressedKeysSeries.indexOf(e.key) === -1) {
pressedKeysSeries.push(e.key);
}
console.log(pressedKeysSeries);
});
I have an html <input> and some pattern (e.g. -?\d*\.?\d* float-signed value).
I should prevent typing the not matched value.
I did it in next way
jQuery.fn.numeric = function (pattern)
{
var jqElement = $(this), prevValue;
jqElement.keydown(function()
{
prevValue = jqElement.val();
})
jqElement.keyup(function(e)
{
if (!pattern.test(jqElement.val()))
{
jqElement.val(prevValue);
e.preventDefault();
}
prevValue = ""
})
};
JSFiddle DEMO
But in this case, value is shown to user and then corrected to right value.
Is it way to vaidate value before it is shown to user?
I can use pattern attribute from html5
$("#validateMe").on('keydown', function() {
var charBeingTyped = String.fromCharCode(e.charCode || e.which); // get character being typed
var cursorPosition = $(this)[0].selectionStart; // get cursor position
// insert char being typed in our copy of the value of the input at the position of the cursor.
var inValue = $(this).value().substring(0, cursorPosition) + charBeingTyped + $(this).value().substring(cursorPosition, $(this).value().length);
if(inValue.match(/-?\d*\.?\d*/)) return true;
else return false;
});
How about this POJS, I'm using a cross-browser addEvent function instead of jquery and not using any regexs, but I believe it achieves what you are looking for. Pressing + or - changes the sign of the value.
HTML
<input id="test" type="text" />
Javascript
/*jslint maxerr: 50, indent: 4, browser: true */
(function () {
"use strict";
function addEvent(elem, event, fn) {
if (typeof elem === "string") {
elem = document.getElementById(elem);
}
function listenHandler(e) {
var ret = fn.apply(null, arguments);
if (ret === false) {
e.stopPropagation();
e.preventDefault();
}
return ret;
}
function attachHandler() {
window.event.target = window.event.srcElement;
var ret = fn.call(elem, window.event);
if (ret === false) {
window.event.returnValue = false;
window.event.cancelBubble = true;
}
return ret;
}
if (elem.addEventListener) {
elem.addEventListener(event, listenHandler, false);
} else {
elem.attachEvent("on" + event, attachHandler);
}
}
function verify(e) {
var target = e.target, // shouldn't be needed: || e.srcElement;
value = target.value,
char = String.fromCharCode(e.keyCode || e.charCode);
if (value.charAt(0) === "-") {
if (char === "+") {
e.target.value = value.slice(1);
}
} else if (char === "-") {
e.target.value = char + value;
return false;
}
value += char;
return parseFloat(value) === +value;
}
addEvent("test", "keypress", verify);
}());
On jsfiddle
I think I used the correct values keyCode || charCode
but you may want to search and check. A summary of the correct ones are available here
You could use this code to find out what character is pressed. Validate that character and, if it validates, append it to the input field.
Try this code:
jQuery.fn.numeric = function (pattern)
{
$(this).keypress(function(e)
{
var sChar = String.fromCharCode(!e.charCode ? e.which : event.charCode);
e.preventDefault();
var sPrev = $(this).val();
if(!pattern.test(sChar)){
return false;
} else {
sPrev = sPrev + sChar;
}
$(this).val(sPrev);
});
};
$("#validateId").numeric(/^-?\d*\.?\d*$/);
jsfiddle.net/aBNtH/
UPDATE:
My example validates each charachter while typing. If you prefer to check the entire value of the input field instead, I would suggest to validate the value on an other Event, like Input blur().