I am trying to use JQuery's each() method to animate typing on multiple blocks of code, but I keep getting this error:
Uncaught TypeError: Cannot call method 'createDocumentFragment' of undefined
Check out some example code in this Fiddle...
And for your convenience, the JS is listed below:
$('.typeanimator').each(function(index) {
console.log(index);
var codeBlock = $(this).text();
var done;
var blockLength = codeBlock.length;
var charCounter = 0;
$(this).text('|');
(function typeAnimator() {
var typingSimulator = Math.round(Math.random() * (200));
done = setTimeout(function() {
console.log("Le print.");
charCounter++;
var typeSection = codeBlock.substring(0, charCounter);
$(this).text(typeSection + '|');
typeAnimator();
if (charCounter == blockLength) {
$(this).text($(this).text().slice(0, -1));
clearTimeout(done);
}
}, typingSimulator);
}());
});
The problem is in the use of $(this) inside the typeAnimator function. You are actually want to refer the this from the parent function but instead you are getting a totally different this. So, use a temporary variable to store the $(this)
$('.typeanimator').each(function(index) {
...
var self = $(this);
self.text('|');
(function typeAnimator() {
var typingSimulator = Math.round(Math.random() * (200));
done = setTimeout(function() {
...
self.text(typeSection + '|');
typeAnimator();
if (charCounter == blockLength) {
self.text(self.text().slice(0, -1));
clearTimeout(done);
}
}, typingSimulator);
}());
});
Updated fiddle
$('.typeanimator').each(function(index, current) {
console.log(index);
var codeBlock = $(current).text();
var done;
var blockLength = codeBlock.length;
var charCounter = 0;
$(current).text('|');
(function typeAnimator(context) {
var typingSimulator = Math.round(Math.random() * (200));
done = setTimeout(function() {
console.log("Le print.");
charCounter++;
var typeSection = codeBlock.substring(0, charCounter);
$(context).text(typeSection + '|');
typeAnimator(context);
if (charCounter == blockLength) {
$(context).text($(context).text().slice(0, -1));
clearTimeout(done);
}
}, typingSimulator);
}(current));
});
This should work, always remember that javascript is really picky about the context when using 'this'. As well .each() has nifty parameter of current item :) Gl, and good code.
Related
I have the below phantomjs program where the website contains a drop down list ddlLevel3
var page = require('webpage').create();
page.onConsoleMessage = function(str) {
console.log(str);
}
var z=0;
var z_l=0;
var op1='#ddlDivision'
var op2='#ddlLevel1'
var op3='#ddlLevel2'
var op4='#ddlLevel3'
function selectOption(selector, optionIndex) {
page.evaluate(function(selector, optionIndex){
var op4='#ddlLevel3'
var sel = document.querySelector(selector);
var sel4 = document.querySelector(op4);
sel.selectedIndex = optionIndex;
var event = document.createEvent("UIEvents"); // See update below
event.initUIEvent("change", true, false);
sel.dispatchEvent(event);
this.dispatchEvent(event);
z_l=sel4.length;
console.log("len: "sel4.length+" "+z_l);
}, selector, optionIndex);
}
page.open(...{
function loop4 () {
selectOption(op4,z);
window.setTimeout(function () {
go();
z++;
if (z < z_l) {
loop4();
}
}, 3000);
}
loop4();
});
I am trying to run loop4(). But the value of z_l is not changing from '0'.
In line console.log("len: "sel4.length+" "+z_l); Here values is proper.
But its not reflected back in loop4() in if (z < z_l), and remains 0 always.
What am I doing wrong?
I I wanted to get the value of z_l updated.I just need to use page.evaluate and assign it to z_l as below. Just made these changes in loop4().
function loop4 () {
var z_l = page.evaluate(function() {
var op4='#ddlLevel3'
return document.querySelector(op4)
});
selectOption(op4,z);
.
.
.
}
I might have missed something obvious here, apologies in advance if so...
Using ajax-solr and implementing my own version of their Current Search widget example/tutorial - https://github.com/evolvingweb/ajax-solr/wiki/Reuters-tutorial%3A-step-5 I have a code snippet which looks like this:
(function($) {
AjaxSolr.CurrentSearchWidget = AjaxSolr.AbstractWidget.extend({
start: 0,
afterRequest: function() {
var self = this;
var links = [];
var q = self.manager.store.get('q').val();
var qText = q;
if (q != null && q != '*:*') {
if (q.split(':').length == 2) {
qText = q.split(':')[1];
}
links.push($('')
.text('search: ' + qText + ' (remove)').click(function() {
localStorage.removeItem("query");
localStorage.setItem("query", "*");
self.manager.store.get('q').val('*:*');
self.doRequest();
return false;
}));
}
var fq = self.manager.store.values('fq');
var prettyText = "";
for (var i = 0, l = fq.length; i < l; i++) {
//string manipulation for user-facing text
links.push($('')
.text(prettyText + ' (remove)').click(self.removeFacet(fq[i])));
//local storage stuff
}
if (links.length > 1) {
links.unshift($('')
.text('remove all').click(function() {
localStorage.clear();
localStorage.setItem("query", "*");
self.manager.store.get('q').val('*');
self.manager.store.remove('fq');
self.doRequest();
return false;
}));
}
//update DOM
},
I've stripped out the unecessary code, but the above works fine. However, if I change
links.push($('')
.text(prettyText + ' (remove)')
.click(self.removeFacet(fq[i])));
to use a function as per the examples above it and below, like
links.push($('')
.text(prettyText + ' (remove)')
.click(function () {
self.removeFacet(fq[i]);
}));
so that I can add additional code there, it no longer runs removeFacet when the anchor is clicked. Thanks for any help on what I'm missing!
This seems to be a problem with function scope. This is not pointing to your object anymore. I would suggest binding your "self" object like so.
links.push($('')
.text(prettyText + ' (remove)')
.click(function () {
var self = this;
self.removeFacet(fq[i]);
}).bind(self));
please see if changing to this works
links.push($('')
.text(prettyText + ' (remove)')
.on('click', function () {
self.removeFacet(fq[i]);
}));
The problem you ran into - you have inner functions (the anonymous click function) that is not executed immediately, therefore the iterator gets "lost". Taking the following example
var i, colors = ['green', 'blue', 'red'];
for (i = 0; i < colors.length; i++) {
var color = colors[i];
setTimeout(function() {
console.log(color);
}, i * 1000);
}
// red, red, red
would log 3 times red, because the code of the function (here inside the timeout) is executed outside the loop, the iterator is at its last position.
Using this variant the value would be captured at each iteration into an argument to a function, which does create a scope, which would produce
the output green, blue, and red
var i, colors = ['green', 'blue', 'red'];
for (i = 0; i < colors.length; i++) {
(function(color) {
setTimeout(function() {
console.log(color);
}, i * 1000);
})(colors[i]);
}
// green, blue, red
In your case something like this should work:
links.push($('')
.text(prettyText + ' (remove)')
.click((function(instance, facet) {
// your code here
// and delete
instance.removeFacet(facet);
})(self, fq[i]));
It's always better to use the on function. Try something like this...
$(window, "a.cur-search", function () {
var self = this;
self.removeFacet(fq[i]);
});
You might need to put fq[i] in data or something to make it available.
I have the following code for a pop-up window (client request). It uses eval which I understand is dangerous. Is there a way to re-write the script below so it does not use (eval)?
/* exported popup_default , popup_help , popup_sitemap , popup_footerlinks */
var matchClass = ['popup_default', 'popup_sitemap', 'popup_footerlinks', 'popup_help'];
var newwindow = ''
var popup_default = 'width=800,height=640,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=250,top=200';
var popup_help = 'width=700,height=650,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=100,top=100';
var popup_sitemap = 'width=1000,height=600,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=100,top=100';
var popup_footerlinks = 'width=800,height=500,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=250,top=200';
function pop_ups() {
"use strict";
var x = 0;
var popClass;
while (x < matchClass.length) {
popClass = "'." + matchClass[x] + "'";
$(eval(popClass)).click(function() {
var popurl = $(this).attr('href');
var popupSpecs = $(this).attr('class');
var popupName = Math.floor(Math.random() * 10000001);
newwindow = window.open(popurl, popupName, eval(popupSpecs));
return false;
});
x++;
}
}
$(function() {
"use strict";
pop_ups();
});
You'll want to use
"use strict";
function makePopup(className, specs) {
$('.'+className).click(function(e) {
e.preventDefault();
var popupName = Math.floor(Math.random()*10000001);
window.open(this.href, popupName, specs);
});
}
var popups = {
popup_default: 'width=800,height=640,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=250,top=200',
popup_help: 'width=700,height=650,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=100,top=100',
popup_sitemap: 'width=1000,height=600,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=100,top=100',
popup_footerlinks: 'width=800,height=500,toolbar=0,menubar=0,location=0,status=1,scrollbars=1,resizable=1,left=250,top=200'
};
$(function() {
for (var key in popups)
makePopup(key, popups[key]);
});
Instead of the first eval, just use string concatenation. Instead of the second eval, use an object to look up a property name.
Thanks #Bergi
I updated that last part of the script because it threw an error: "The body of a for in should be wrapped in an if statement"
$(function() {
"use strict";
for (var key in popups) {
if(popups.hasOwnProperty(key))
{
makePopup(key, popups[key]);
}
}
});
Thanks again for the help
I have jQuery code to resize several Iframes based on the container they are in. When I did this without the .each() operator All of the iframes used the same div to base their size off of. With .each() the operator runs as evidenced by an alert but does not continue down the function.
Codepen: https://codepen.io/bjackson2016/pen/oLQopy?editors=0010
$(document).ready(function(){
function resize() {
var elements = $('.resize');
var eachElement = $.makeArray(elements);
alert(eachElement);
$.each(eachElement, function(index, value) {
alert(index + " " + value);
var multi = value.data('wfactor');
alert(multi);
var ratio = value.data('ratio');
alert(ratio);
var minX = value.data('minw');
alert(minX);
var minY = value.data('minh');
alert(minY);
var ruler = value.siblings('.widthTest');
alert(ruler);
var pWidth = ruler.width();
alert(pWidth);
var adjWidth = pWidth * multi;
alert(adjWidth);
var eHeight = (Math.round(ratio * adjWidth));
alert(eHeight);
var unadjHeight = Math.round(ratio * pWidth);
alert(unadjHeight);
var eWidth = (Math.round(adjWidth));
alert(eWidth);
if (eHeight < minY) {
$(this).height(minY);
alert('changed height');
} else {
value.height(eHeight);
alert('normal height');
}
if (eWidth < minX) {
value.css('width', pWidth).css('height', unadjHeight);
alert('changed width');
} else {
value.width(eWidth);
alert('kept width');
}
});
}
resize();
$(window).resize(function() {
resize();
});
});
The problem is that there is no value.data().
data() is a jQuery function, and iterating with $.each unwraps the elements, so you're trying to call data() on a native DOM node
$.each(eachElement, function(index, value) {
// value is not a jQuery object here, you'd have to do
var elem = $(value); // where you wrap it again.
});
Try instead
$(document).ready(function() {
$(window).on('resize', resize).trigger('resize');
function resize() {
$('.resize').each(function(index, element) {
var elem = $(element);
var multi = elem.data('wfactor'),
ratio = elem.data('ratio'),
... etc
});
}
});
I have two functions. In the first one I increase a variable by adding 100 to it and I put a setInterval so the funcion repeats itself after some time. The other function is a class, a contrusctor to create an object. I want this.x_origen to get increased by adding aumento to it after some time and repeat it. However what I'm getting here is that the first function increases aument and then it finishes and then the second function starts. How can I solve this?
var aument = 0;
function aumento(){
aument = aument + 100;
return aument;
}
setInterval(function () {aumento()}, 1000/50);
function create_class_brick (x_origen_in, y_origen_in, x_final_in, y_final_in, mi_estado, mi_velocidad, mi_id){
this.x_origen = x_origen_in + aumento();
this.y_origen = y_origen_in;
this.x_final = x_final_in + aumento();
this.y_final = y_final_in;
this.estado = mi_estado;
this.velocidad = mi_velocidad;
this.id_elemento = mi_id;
this.DESPLAZAR_LADRILLO = desplazar_ladrillo;
this.F0 = f0;
this.F2 = f2;
this.crear_ladrillo = crear_ladrillo;
this.obtener_x_origen_ladrillo = obtener_x_origen_ladrillo;
this.obtener_y_origen_ladrillo = obtener_y_origen_ladrillo;
this.obtener_x_final_ladrillo = obtener_x_final_ladrillo;
this.obtener_y_final_ladrillo = obtener_y_final_ladrillo;
}
An example on how to wait for the initial call:
function brick (x_origen_in){
this.x_origen = x_origen_in;
}
function aumento(brick){
console.log(brick.x_origen);
brick.x_origen += 100;
setTimeout(aumento.bind(this, brick), 500);
}
var brick = new brick(100);
aumento(brick);
http://jsfiddle.net/x6c08u39/
You can use Object.defineProperty to dynamically generate the value whenever it is accessed.
First, lets simplify the auto-incrementing of aument:
var aument = 0;
function aumento(){
aument += 100;
}
// The first argument for setInterval is the function to execute
// No need to figure out the interval value at runtime as there are no dynamic values
setInterval(aumento, 20); // 1000/50 === 20
Now lets make an object that will have a the correct value:
function create_class_brick (x_origen_in, y_origen_in, x_final_in, y_final_in, mi_estado, mi_velocidad, mi_id){
Object.defineProperty(this, 'x_origen', {
get: function () { return x_origen_in + aument; }
});
// Other stuff
// ...
}
A quick test:
> aument
34100
> var obj = new create_class_brick(23);
undefined
> obj.x_origen
161523
> obj.x_origen
167223
> obj.x_origen
172423