How to undo and redo onclick in svg element? - javascript

I have svg text fill which is dynamic. once the user click undo button it must undo the svg and textarea and once the user click redo button it should redo the svg text fill and textarea. i have completed with the textarea undo and redo functionality but not with svg element how to achieve this through jquery
$("#enter-text").on("keypress",function(){
$("#svg_id").html($(this).val());
})
//this value is kept small for testing purposes, you'd probably want to use sth. between 50 and 200
const stackSize = 10;
//left and right define the first and last "index" you can actually navigate to, a frame with maximum stackSize-1 items between them.
//These values are continually growing as you push new states to the stack, so that the index has to be clamped to the actual index in stack by %stackSize.
var stack = Array(stackSize), left=0, right=0, index = 0, timeout;
//push the first state to the stack, usually an empty string, but not necessarily
stack[0] = $("#enter-text").val();
updateButtons();
$("#enter-text").on("keydown keyup change", detachedUpdateText);
$("#undo").on("click", undo);
$("#redo").on("click", redo);
//detach update
function detachedUpdateText(){
clearTimeout(timeout);
timeout = setTimeout(updateText, 500);
}
function updateButtons(){
//disable buttons if the index reaches the respective border of the frame
//write the amount of steps availabe in each direction into the data- count attribute, to be processed by css
$("#undo")
.prop("disabled", index === left)
.attr("data-count", index-left);
$("#redo")
.prop("disabled", index === right)
.attr("data-count", right-index);
//show status
$("#stat").html(JSON.stringify({
left,
right,
index,
"index in stack": index % stackSize,
stack
}, null, 4))
}
function updateText(){
var val = $("#enter-text").val().trimRight();
//skip if nothing really changed
if(val === stack[index % stackSize]) return;
//add value
stack[++index % stackSize] = val;
//clean the undo-part of the stack
while(right > index)
stack[right-- % stackSize] = null;
//update boundaries
right = index;
left = Math.max(left, right+1-stackSize);
updateButtons();
}
function undo(){
if(index > left){
$("#enter-text").val(stack[--index % stackSize]);
updateButtons();
}
}
function redo(){
if(index < right){
$("#enter-text").val(stack[++index % stackSize]);
updateButtons();
}
}
https://jsfiddle.net/yvp3jedr/6/

$("#enter-text").on("keypress", function () {
$("#svg_id").text($(this).val());
})
//this value is kept small for testing purposes, you'd probably want to use sth. between 50 and 200
const stackSize = 10;
//left and right define the first and last "index" you can actually navigate to, a frame with maximum stackSize-1 items between them.
//These values are continually growing as you push new states to the stack, so that the index has to be clamped to the actual index in stack by %stackSize.
var stack = Array(stackSize), left = 0, right = 0, index = 0, timeout;
//push the first state to the stack, usually an empty string, but not necessarily
stack[0] = $("#enter-text").val();
updateButtons();
$("#enter-text").on("keydown keyup change", detachedUpdateText);
$("#undo").on("click", undo);
$("#redo").on("click", redo);
//detach update
function detachedUpdateText() {
clearTimeout(timeout);
timeout = setTimeout(updateText, 500);
}
function updateButtons() {
//disable buttons if the index reaches the respective border of the frame
//write the amount of steps availabe in each direction into the data-count attribute, to be processed by css
$("#undo")
.prop("disabled", index === left)
.attr("data-count", index - left);
$("#redo")
.prop("disabled", index === right)
.attr("data-count", right - index);
//show status
$("#stat").html(JSON.stringify({
left,
right,
index,
"index in stack": index % stackSize,
stack
}, null, 4))
}
function updateText() {
var val = $("#enter-text").val().trimRight();
//skip if nothing really changed
if (val === stack[index % stackSize]) return;
//add value
stack[++index % stackSize] = val;
//clean the undo-part of the stack
while (right > index)
stack[right-- % stackSize] = null;
//update boundaries
right = index;
left = Math.max(left, right + 1 - stackSize);
updateButtons();
}
function undo() {
if (index > left) {
var text = stack[--index % stackSize];
$("#enter-text").val(text);
$("#svg_id").text(text);
updateButtons();
}
}
function redo() {
if (index < right) {
var text = stack[++index % stackSize];
$("#enter-text").val(text);
$("#svg_id").text(text);
updateButtons();
}
}

Related

"Uncaught TypeError:<function name> is not a function" after it got used once

I have a function, to make a div-Box "jump". The function works the first time, but after that I get the error "Uncaught TypeError: jump is not a function" after it gets used once. Can someone please explain why it doesn't work?
already = false;
function jump() {
if (already == false) { //So he can't jump 2 times in a row
try {
clearInterval(t); //<--this is my gravity function, where the div-Box falls down until it hits solid ground
} catch (err) {
console.log("not activated");
}
jump = setInterval("jump2();", 200);
already = true;
}
}
}
anz = 0;
function jump2() {
//Getting coordinates of div-Boy(they work)
var step = 10;
var bottom = getBottom("itBoy");
var right = getRight("itBoy");
var top = getTop("itBoy");
var left = getLeft("itBoy");
//lets see if he hits an object
if (anz <= 100) { //<-- anz = so he cant jump higher than 100 px
if (top - step >= 0) {
var a = hittest("itBoy", "up", 10); //if div wont hit a solid object --> return -1 | else return coordinates of bordes which collide (this function works too)
if (a == -1) {
anz += step;
document.getElementById("itBoy").style.top = (top -= step) + "px";
} else {
document.getElementById(itBoy).style.top = a + "px";
clearInterval(jump); // div stops moving upwards
t = setInterval("move('down');", 50); //gravity gets Activated again
}
}
} else {
clearInterval(jump);
t = setInterval("move('down');", 50);
}
}
It's because, you're overriding the jump:
function jump(){
// ...
jump = setInterval("jump2();",200);
// ^^ give it a different name
Also, a good approach to use function inside setInterval like:
setInterval(jump2, 200); // faster

looping through array content with a delay

this is a jquery/ javascript problem.
So I have an array that contains button numbers and outputs those buttons onto a button which will result in the button being clicked. The problem is that all the buttons get clicked at once if I run the loop. Instead I want it to output the numbers with a delay so that the buttons will be pressed after a 1 second delay.
Here is the link to the Simon game project:
https://codepen.io/roger1891/full/vmYqwx/
The problem is visible after following the 1st button. After that, the computer will press the next 2 buttons at the same time instead of pressing them separately after a 1 sec delay.
The problem is located in this loop which is located in the myloop() function:
sequenceArray.forEach(function(item, index, array){
//click button by getting the last index of the array
//$("#btn-"+array[array.length-1]).click();
$("#btn-"+array[index]).click();
console.log(array);
});
This is the full code:
//associating buttons to sound
var soundButton = function(buttonId, soundId) {
$("#" + buttonId).click(function(){
$("#" + soundId).get(0).play();
$("#" + buttonId).css("opacity", "0.5");
setTimeout( function(){
$("#" + buttonId).css("opacity", "1");
},500);
if(currentPlayerTurn == "human") {
var $input = $(this);
var attrString = $input.attr("id");
//only get number from id attribute
var strNum = attrString.replace( /^\D+/g, '');
//convert theNumber from string to number
var theNum = parseInt(strNum);
playerArray.push(theNum);
console.log(theNum);
console.log(playerArray);
}
});
};
function myLoop() {
setInterval( function(){
if(gameWon == false && onGoingGame == true && currentPlayerTurn == "computer" && score < 5) {
//increment score
score++;
//append to score id
$("#score").empty().append(score);
//create random number
randomNumber = Math.floor((Math.random()*4) + 1);
//push random number into array
sequenceArray.push(randomNumber);
//loop through array
sequenceArray.forEach(function(item, index, array){
//click button by getting the last index of the array
//$("#btn-"+array[array.length-1]).click();
$("#btn-"+array[index]).click();
console.log(array);
});
currentRound = sequenceArray.length;
onGoingGame = false;
currentPlayerTurn = "human";
}
if(currentPlayerTurn == "human") {
var is_same = playerArray.length == sequenceArray.length && playerArray.every(function(element, index) {
return element === sequenceArray[index];
});
is_same;
console.log(is_same);
if(is_same == true) {
playerArray = [];
onGoingGame = true;
currentPlayerTurn = "computer";
}
}
},1000);
}
myLoop();
Thank you in advance for your help.
Since you want to trigger the buttons one by one, your setTimeout() should be inside the loop. Be mindful of the index, since this is async.
sequenceArray.forEach(function(item, index, array) {
// set a timeout so each second one button gets clicked
setTimeout( (function( index ) {
// use a closure here so that our index values stay correct.
return function() {
$("#btn-"+array[index]).click();
};
}( index )), (1000 * index) );
});
edit: replaced the fixed 1000ms delay to delay * index
You need to console.log(item) instead of full array in forEach loop

Scroll to pure javascript not working

Yes I am avoiding a 3rd party lib on purpose, very small app on mobile device and don't want to pull anything.
I am trying to scroll to elements on a page using this scrollTo function:
function scrollTo(element, to, duration) {
if (duration <= 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() {
element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) return;
scrollTo(element, to, duration - 10);
}, 10);
}
I grab a list of span tags that I want to use as my scroll points each time the user clicks a next button:
var index = null;
function next() {
var list = document.querySelectorAll("span");
var element = null;
if (index == null) {
index = -1;
element = document.body;
} else {
element = list[index];
}
index++;
if (index >= list.length) {
return;
}
var to = list[index];
scrollTo(element, to.offsetTop, 250);
}
The first next click properly scrolls to the first element. However the next click does not scroll. I debugged into the scrollTo method and it seems the element.scrollTop variable is not changing each time it is assigned. Any ideas?
You musst change the element.scrollTop of the element you want to scroll on, in this case document.body. You are only doing this in the first call. After that you´re always scrolling on the list item which is your target (what has no effect, since it has no overflow).
You should call your scrollTop like this:
scrollTo(document.body, to.offsetTop, 250);
Maybe you should also take a look at
window.scrollTo()
https://developer.mozilla.org/en/docs/Web/API/Window/scrollTo
I was not able to replicate your exact issue, but what I did find was that I was not able to get to the last span with the way your code was written. To get around that, I rewrote a few things. It may fix the issue for you.
function next() {
var list = document.querySelectorAll("span");
var element = null;
if (index == null) {
index = -1;
element = document.body;
} else {
element = list[index];
}
if (index >= list.length) {
return;
}
if(index >= 0)
{
var to = list[index];
scrollTo(element, to.offsetTop, 250);
}
index++;
}
Essentially, you were trying to increment your index too soon causing it to never process the last span.

Nonconcurrent async recursion

My end goal is to mitigate as much lag (window freezing/stuttering) as possible, giving the client a responsive window from page load.
My program is a Chrome extension, and part of it needs to search through a reddit submission, including all comments for certain words and then do some stuff with them. After this answer, I converted my code to use setInterval for the recursive search. Unforutnately, this runs concurrently, so even though each branch in the comment tree is delayed from its parent, the overall search overlaps each other, negating any benefit in the delay.
I have a solution, but I don't know how to implement it.
The solution would be to have a callback when a branch runs out that goes to the nearest parent fork. This in effect would traverse the comment tree linearly and would allow the setInterval (or probably setTimeout would be more appropriate) to have a noticeable affect.
The code that would need to be changed is:
function highlightComments(){
var elems = $(".content .usertext-body > .md");
var index = 0;
var total = elems.length;
console.log("comments started");
var intId = setInterval(function(){
highlightField(elems.get(index));
index++;
if(index == total){
clearInterval(intId);
addOnClick();
console.log("comments finished");
}
}, 25);
}
and highlightField is:
function highlightField(node) {
var found = $(node).attr("data-ggdc-found") === "1";
var contents = $.makeArray($(node).contents());
var index = 0;
var total = contents.length;
if (total == 0){
return;
}
var intId = setInterval(function() {
if (contents[index].nodeType === 3) { // Text
if (!found){
//Mods
var content = contents[index].nodeValue.replace(new RegExp(data.mods.regex, "gi"), data.mods.replacement);
//Creators
content = content.replace(new RegExp(data.creators.regex, "gi"), data.creators.replacement);
//Blacklist
for (var key in data.blacklist.regex){
if(data.blacklist.regex.hasOwnProperty(key)){
content = content.replace(new RegExp(data.blacklist.regex[key], "gi"), data.blacklist.replacement[key]);
}
}
if (content !== contents[index].nodeValue) {
$(contents[index]).replaceWith(content);
}
}
} else if (contents[index].nodeType === 1) { // Element
highlightField(contents[index]);
}
index++;
if(index == total){
clearInterval(intId);
}
}, 25);
}

Javascript - How do I improve performance when animating integers?

On the site I'm working on, we collect data from a datasource and present these in their own design element, in the form of an SVG. The SVG files are renamed to .php in order to insert the dynamic data from the datasource.
Then, I'm using inview javascript to initialize a function that animates the data from the source, from 0 to their actual value. However, I notice this gets kinda heavy on the browser, when there are a lot of elements that are running the animate function.
Is there perhaps a smarter way of doing this? I haven't really dug that much into it, because it's not that bad. I just happened to notice the lag when scrolling through the area being repainted.
Here's my js code:
$('.inview article').bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
if (isInView) {
// element is now visible in the viewport
if (visiblePartY == 'top' || visiblePartY == 'both' || visiblePartY == 'bottom') {
var $this = $(this);
var element = $($this);
$this.addClass('active');
reinitializeText(element);
$this.unbind('inview');
// top part of element is visible
} else if (visiblePartY == 'bottom') {
} else {
}
} else {
}
});
function reinitializeText(element) {
var svg = element.find('svg');
var children = svg.children('.infographics_svg-text');
// If there is no class in svg file, search other elements for the class
if (children.length == 0) {
var children = element.find('.infographics_svg-text');
}
children.each(function (){
var step = this.textContent/100;
var round = false;
if (this.textContent.indexOf('.') !=-1) {
round = true;
}
animateText(this, 0, step, round);
});
}
function animateText(element, current, step, round) {
if (current > 100) return;
var num = current++ *step;
if (round) {
num = Math.round((num)*100)/100
} else {
num = Math.round(num);
}
element.textContent = num;
setTimeout(function() {
animateText(element, current, step, round);
}, 10);
}
Edit: Because of the difference in data values received from the source (low numbers to huge numbers), The speed of the animation is increased so it doesn't go on forever

Categories

Resources