How to apply function to all elements of a class - javascript

I have a function that I'd like to apply to several elements of the same class. It's a scroll page function and I need it to only execute once. So I put it in a wrapper. It works but I'd like to be able to just add a class to an element and have it act upon that element. I tried iterating through the elements and using addClass to add a unique class with their respective index added to the end but this did not work. What I have now only acts upon the first element with the "split" class.
//EXECUTES ONLY ONCE
function once(fn, context) {
var result;
return function() {
if(fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
// Usage
//var split1 = once(function() {
// fadeInText(".split1");
//});
const handlers = $(".split").toArray()
.map(s => ({ el: $(s), show: once(() => fadeInText(s)) }));
$(window).scroll(function() {
for(const {el, show} of handlers) {
if( $(this).scrollTop() + $(window).height() > el.offset().top)
show();
}
});
//SPLITTEXT
var f = ".split",
fadeInText(f);
function fadeInText(l) {
var el = document.querySelector(l);
var split = el.dataset.split;
var text = new SplitText(el, { type: split });
var tl = new TimelineMax({ paused: false });
var splitEls = text[split];
var wrapEls = function wrapEls(els) {
return els.map(function (el) {
return '<span style="display: inline-block">' + el.innerText + '</span>';
});
};
var wrapped = wrapEls(splitEls);
splitEls.forEach(function (el, i) {
el.style.overflow = 'hidden';
el.innerHTML = wrapped[i];
});
var masks = splitEls.map(function (el) {
return el.querySelector('span');
});
tl.staggerFrom(masks, 1.25, { skewY: 4, y: '200%', ease: Expo.easeOut, delay: 0.9 }, 0.1, 'in');
return l;
}

if you want a pure js application try this:
elements = document.querySelectorAll('.class');
elements.forEach((element, key) => {
//your code
})

$('.class').each(function(){ /* 'this' means the element */})
Inside the function, this is scoped to the element you're on in the iteration.

Related

Join function in JavaScript with separator not showing at the end for arrays with unknown number of elements

I am new to JavaScript and I have encountered a little problem while using the join function. I have an array with an unknown number of elements and I want to print it with separators between each two elements using join. I want to get something like this: a/b/c/d with '/' being the separator. The problem is that I get one last separator at the end, like this: a/b/c/d/. How can I get rid of this?
Here is my code:
var makePath;
function makePath(separator) {
let comp = []
return function(element) {
comp.push(element)
return comp.join(separator)
}
}
var main = function() {
var p1 = makePath("/");
p1("One");
p1("Two");
p1("Three");
window.console.log("p1:" + p1());
}
main()
in your console.log statement you are invoking p1() with an empty argument. That is what is adding the trailing delimiter to your string. You would want to store the result of the final invocation and use that instead.
var makePath;
function makePath(separator)
{
let comp = []
return function(element)
{
comp.push(element)
return comp.join(separator)
}
}
var main = function()
{
var p1 = makePath("/");
p1("One");
p1("Two");
var x = p1("Three");//store the result of p1('three')
window.console.log("p1:" + x );//print it out
}
main();
This happens because when you call p1() without an argument, you're actually performing p1(undefined). Therefore your array looks like this : ["One", "Two", "Three", undefined] and gets joined as One/Two/Three/
Demonstration :
var makePath;
function makePath(separator) {
let comp = []
return function(element) {
console.log(`pushing "${element}" to array`)
comp.push(element)
return comp.join(separator)
}
}
var main = function() {
var p1 = makePath("/");
p1("One");
p1("Two");
p1("Three");
window.console.log("p1:" + p1());
}
main()
Solution : if no argument is passed, don't push it to the array :
var makePath;
function makePath(separator) {
let comp = []
return function(element) {
if(element) comp.push(element)
return comp.join(separator)
}
}
var main = function() {
var p1 = makePath("/");
p1("One");
p1("Two");
p1("Three");
window.console.log("p1:" + p1());
}
main()
Why not only check for the undefined param? and that way you execute the function join only when you need to build the string.
function makePath(separator) {
let comp = []
return function(element) {
if (element) comp.push(element);
return comp.join(separator);
}
}
var main = function() {
let pathify = makePath("/");
pathify("One");
pathify("Two");
pathify("Three");
window.console.log("Path:", pathify());
}
main()
just add .replace which will remove last / with this regexp /\/$/
var makePath;
function makePath(separator)
{
let comp = []
return function(element)
{
comp.push(element)
return comp.join(separator).replace(/\/$/,"")
}
}
var main = function()
{
var p1 = makePath("/");
p1("One");
p1("Two");
p1("Three");
window.console.log("p1:" + p1() );
}
main();

Issue with translation of static website

I have a static html5 and css3 website, which I want to translate. Primary language is German, and I want to translate it to English and Russian.
I found a jQuery plugin (translate.js), which does exactly what I need but I have one issue with this plugin.
Everything works perfectly, when I use for example
HTML
<p class="trn">Herzlich Willkommen</p>
jQuery:
$(function() {
var t = {
"Herzlich Willkommen": {
en: "Welcome",
ru: "Добро пожаловать"
}
};
var _t = $('body').translate({
lang: "de",
t: t
});
var str = _t.g("translate");
console.log(str);
$(".lang_selector").click(function(ev) {
var lang = $(this).attr("data-value");
_t.lang(lang);
console.log(lang);
ev.preventDefault();
});
});
but when I want to use nested trn class inside parent trn class, unfortunately I can not translate it.
For example:
<h2 class="trn">Ihr <span class="trn">Bauunternehmen</span> in <span class="trn">Wien</span></h2>
Can you help me please?
Plugin documentation
jQuery.translate.js
(function($) {
$.fn.translate = function(options) {
var that = this; //a reference to ourselves
var settings = {
css: "trn",
lang: "en"
/*,
t: {
"translate": {
pt: "tradução",
br: "tradução"
}
}
*/
};
settings = $.extend(settings, options || {});
if (settings.css.lastIndexOf(".", 0) !== 0) //doesn't start with '.'
settings.css = "." + settings.css;
var t = settings.t;
//public methods
this.lang = function(l) {
if (l) {
settings.lang = l;
this.translate(settings); //translate everything
}
return settings.lang;
};
this.get = function(index) {
var res = index;
try {
res = t[index][settings.lang];
} catch (err) {
//not found, return index
return index;
}
if (res)
return res;
else
return index;
};
this.g = this.get;
//main
this.find(settings.css).each(function(i) {
var $this = $(this);
var trn_key = $this.attr("data-trn-key");
if (!trn_key) {
trn_key = $this.html();
$this.attr("data-trn-key", trn_key); //store key for next time
}
$this.html(that.get(trn_key));
});
return this;
};
})(jQuery);

Unable to trigger click function in own custom plugin

I am trying to write own custom plain Javascript plugin.
Here is my sample plugin code:
(function() {
var pMethods = {
append: function(text) {
var node = this.node;
node.innerHTML += text;
},
click: function(fn) {
if (this.node instanceof Array) {
this.node.forEach(function(e) {
e.addEventListener('click', function() {
fn();
});
}, this);
} else {
this.node.addEventListener('click', function(e) {
fn(e);
});
}
}
};
myPlugin = function(selector) {
this.node = document.querySelectorAll(selector);
if (this.node.length === 1) {
this.node = this.node[0];
}
return this.node;
};
myPlugin.prototype = pMethods;
this.r = function(selector) {
return new myPlugin(selector);
};
}());
which has just two function append and click.
Here is my HTML:
<div class="close"></div>
Now I am trying to add click event on close div as follow:
r('.close').click(function() {
alert('Hi')
});
but it is not working as expected and I don't know what I'm missing here.
Your code did not work because you were explicitly checking if your element collection is an Array. Any element collection returned will be a NodeList which is an array like object, but not an array.
if (this.node instanceof Array)
should be
if (this.node instanceof NodeList)
Or you could use Array.prototype.slice to convert the NodeList to an Array
this.node = Array.prototype.slice.call(
document.querySelectorAll(selector)
)
Here are a couple of optimisations.
(function() {
var pMethods = {
append: function(text) {
// iterate over the collection
this.nodes.forEach(function(node) {
node.innerHTML += text;
})
// return this for chaining
return this
},
click: function(fn) {
// iterate over the collection
this.nodes.forEach(function(e) {
e.addEventListener('click', fn);
});
// return this for chaining
return this
},
find: function(selector) {
return new myPlugin(
// flat map over each of the nodes in the collection
this.nodes.reduce(function(nodes, node) {
return [].concat.apply(nodes, node.querySelectorAll(selector))
}, [])
)
}
};
myPlugin = function(nodes) {
// changed constructor to recievea array of elemnets only
// it's private so won't affect anything else
this.nodes = nodes
};
myPlugin.prototype = pMethods;
this.r = function(selector) {
var nodes = null
// handle creating the object with normal elements
if (selector instanceof HTMLElement) {
nodes = [selector]
}
else {
nodes = [].slice.call(
document.querySelectorAll(selector)
);
}
return new myPlugin(nodes);
};
}());
r('.close')
.click(function(e) {
console.log('alerts suck! ' + e.target.textContent)
r(e.target).find('.child').append(' appended child!')
})
.append(' append works!')
<div class="close">
close
<div class="child">this is the child</div>
</div>
your constructor function (e.g. myPlugin = function(selector) {) should return this instead of this.node

How do I invoke my function on keyup?

I have a JavaScript function that I want to fire once the user enters text inside an input element. Currently I can only see the function firing if I console.log it. How do I get it to fire using keyup method?
The relevant code is below.
var $ = function (selector) {
var elements = [],
i,
len,
cur_col,
element,
par,
fns;
if(selector.indexOf('#') > 0) {
selector = selector.split('#');
selector = '#' + selector[selector.length -1];
}
selector = selector.split(' ');
fns = {
id: function (sel) {
return document.getElementById(sel);
},
get : function(c_or_e, sel, par) {
var i = 0, len, arr = [], get_what = (c_or_e === 'class') ? "getElementsByClassName" : "getElementsByTagName";
if (par.length) {
while(par[I]) {
var temp = par[i++][get_what](sel);
Array.prototype.push.apply(arr, Array.prototype.slice.call(temp));
}
} else {
arr = par[get_what](sel);
}
return (arr.length === 1)? arr[0] : arr;
}
};
len = selector.length;
curr_col = document;
for ( i = 0; i < len; i++) {
element = selector[i];
par = curr_col;
if( element.indexOf('#') === 0) {
curr_col = fns.id(element.split('#'[1]));
} else if (element.indexOf('.') > -1) {
element = element.split('.');
if (element[0]) {
par = fns.get('elements', element[0], par);
for ( i =0; par[i]; i++) {
if(par[i].className.indexOf(element[1]> -1)) {
elements.push(par[i]);
}
}
curr_col = elements;
} else {
curr_col = fns.get('class', element[1], par);
}
} else {
curr_col = fns.get('elements', element, par);
}
}
return elements;
};
You need to bind your method to the keyup event on the page.
You could try
document.addEventListener('keyup', $)
Or assuming you have the input element as element you could do
element.addEventListener('keyup', $)
Your function will be passed the event which you could use to investigate the state of the element if you needed that information to trigger or not trigger things in the function.
Here's a quick sample where the function that get's run on keypress is changeColor.
var COLORS = ['red', 'blue','yellow', 'black']
var NCOLORS = COLORS.length;
function changeColor(ev) {
var div = document.getElementById('colored');
var colorIdx = parseInt(Math.random() * NCOLORS);
console.log(colorIdx);
var newColor = COLORS[colorIdx];
div.style.color = newColor
console.log("New color ", newColor)
}
document.body.addEventListener('keyup', changeColor)
Though I'm not using the event (ev), I like to show, in the code, that I expect that variable to be available.
See it in action here - http://codepen.io/bunnymatic/pen/yyLGXg
As a sidenote, you might be careful about calling your function $. Several frameworks (like jQuery) use that symbol and you may run into conflicts where you're overriding the global variable $ or where the framework overrides your version if it.

Stripping html tags, 'RegExp-free-way'

I've got a bit paranoid lately about the solutions out there that deal with the task by using regular expressions to 'sanitize' html strings. They largely depend on how 'bullet-proof' given regex is. So, I came up with this snippet and hope to get some feedback about it from community. Thanks.
//
// #notags
String.prototype.notags = (function (doc) {
var mkel = doc.createElement.bind(doc);
var hasown = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
// #textlike-nodes
var textlike = {
12 : "NOTATION_NODE",
3 : "TEXT_NODE",
4 : "CDATA_SECTION_NODE",
5 : "ENTITY_REFERENCE_NODE",
6 : "ENTITY_NODE",
7 : "PROCESSING_INSTRUCTION_NODE",
8 : "COMMENT_NODE"
};
// #_notags
// main function
var _notags = function (tagedstring) {
var div;
var istxt = istextnode;
var nodes;
var nodetxt = getxt;
var res;
div = mkel('div');
div.innerHTML = (''+ tagedstring);
// get the div's descendants
// and read their text content
// until all of its childern are plain
// text nodes...
nodes = descendants(div);
while (!nodes.every(istxt)) {
div.innerHTML = nodetxt(div);
nodes = descendants(div);
}
res = div.innerHTML;
// get rid of temporary div
// prevents mem. leaks
div.innerHTML = '';
delete div;
return res;
};
// #save
// String#notags
return function () {
return _notags(this);
};
////////////////
////// #helpers
// #istextnode
function istextnode (node) {
return !(3 - node.nodeType);
}
// #descendants
function descendants (startnode, _nodels) {
_nodels || (_nodels = []);
var node = startnode.firstChild;
while (node) {
_nodels.push(node);
descendants(node, _nodels);
node = node.nextSibling;
}
return _nodels;
}
// #getxt
// loop each node's descendant
// and fetch it' text content
function getxt (node) {
var _ = {
str: '',
txt: textlike
};
descendants(node)
.forEach(getnodetext, _);
return _.str;
}
// #getnodetext
function getnodetext (node) {
//this: {str, txt}
if (hasown(this.txt, node.nodeType))
this.str += (node.data || node.textContent || node.nodeValue);
}
})(document);
// /eof

Categories