How to .slice changing parts of a string in jQuery - javascript

I have several divs that use bootstrap Grid system to allow users of the site to "zoom" content.
In this example with the class="abcd b1 col-lg-6" everything works fine.
But then I have my b1 part of the classes.. it will go up beyond b10, which makes my code no longer work.
I have tried to change the code so it would read from right to left... but then when the col-lg-6 becomes col-lg-10 it no longer works either.
So my question: is there a better way to .slice the code so it will work even when my classes length changes?
<div id="col" class="abcd b1 col-lg-6"> //the div
<div class="tn bg1">
<div class="ca">
<div class="bp">
<button type="button" id="plus" class="btn" data-toggle="tooltip" data-placement="left" title="Bigger Size"></button> //the button
<button type="button" id="minus" class="btn" data-toggle="tooltip" data-placement="left" title="Smaller Size"></button>
</div>
</div><!--ca-->
</div><!--tn-->
</div><!--col-->
<!--Plus Button-->
var className = ('col-lg-'); //make className = "col-lg-"
$('button#plus').click(function() { //find button#plus and add clickfunction on it.
$this = $(this).closest('div#col'); //find the div that shall change on click, closest div with id=col
var counter = $this.attr('class').split(className)[1]; //get the last section of the class. class "col-lg-6" will set the counter to = 6
var classList = $($this).attr('class').split(/\s+/); //get all the classes of that div, in an objekt array stored in "classList3"
var classString = classList.toString(); //make that objekt array into a string. classstring = "abcd,b1,col-lg-6"
var oldClass = classString.slice(8); //slice out the part i want. oldClass = "col-lg-6"
var nextNum = 1;
if (counter != '') //if the counter has already started
nextNum = parseInt(counter) + 2; //add +2 on click
if (counter < 12) { //if counter are less then 12
$this.removeClass(oldClass).addClass(className + nextNum); //Remove class "col-lg-6" (or what ever the number are at the moment)
}
else { //if counter are bigger or = 12
//do nothing
}
})

I found a way to fix it :)
First i add the variable:
var otherClasses = $this.attr('class').split(className)[0];
This variable store the other classes " abcd b1 ", not the " col-lg-6 "
Then i changed this:
$this.removeClass(oldClass).addClass(className + nextNum);
To this:
$this.removeClass().addClass(otherClasses + className + nextNum);
This will remove all the classes and then add the old ones + the new one back.
And remove this completly, there are no need for it anymore.
var classList = $($this).attr('class').split(/\s+/);
var classString = classList.toString();
var oldClass = classString.slice(8);

Related

For loop not working as expected with string replacement

I am trying to make a javascript webextension that adds a couple of numbers eg. "123" to the end of the inner text of a hyperlink text to each product on a shopping website, eg. http://www.tomleemusic.ca
For example, if I go to this link, http://tomleemusic.ca/catalogsearch/result/?cat=0&q=piano
I want to add the item's identification number to the end of the product's name.
name of product and the href tag are in its item link, tomleemusic.ca/xxxxxx with the x's being the item number
However with my following code, I simply append the item number of the first item in the list to every item, instead of a different item number for each item.
var productsListLink = document.querySelectorAll(".products-grid .item .product-name a:not(.product-image)");
for (var i = 0; i < productsListLink.length; i++) {
var a = productsListLink[i];
var name = a.innerHTML || "";
var addon = document.querySelector(".products-grid .item .product-name a:not(.product-image)").getAttribute('href');
var newaddon = addon.replace("http://tomleemusic.ca/","");
name += newaddon;
a.innerHTML = name;
a.setAttribute('title', name);
}
In this line, you're grabbing only the first matching element:
var addon = document.querySelector(".products-grid .item .product-name a:not(.product-image)").getAttribute('href')
You already have the element you're actually working with in each loop iteration in a; just use that instead:
var addon = a.getAttribute('href')
Example:
var productsListLink = document.querySelectorAll(".products-grid .item .product-name a:not(.product-image)");
for (var i = 0; i < productsListLink.length; i++) {
var a = productsListLink[i];
var name = a.innerHTML || "";
var addon = a.getAttribute('href');
var newaddon = addon.replace("http://tomleemusic.ca/","");
name += newaddon;
a.innerHTML = name;
a.setAttribute('title', name);
}
<div class="products-grid">
<div class="item">
<span class="product-name">
</span>
</div>
<div class="item">
<span class="product-name">
</span>
</div>
<div class="item">
<span class="product-name">
</span>
</div>
</div>
querySelector will always return the first matching element. Thus, when you do
var addon = document.querySelector(".products-grid .item .product-name a:not(.product-image)").getAttribute('href');
you're selecting the first a (the one you get in your first iteration).
But, you can make the code a whole lot cleaner by using array methods and a regular expression to match the id:
Array.prototype.forEach.call(
document.querySelectorAll(".products-grid .item .product-name a:not(.product-image)"),
(productNameElement) => {
const idMatch = productNameElement.href.match(/\d+$/);
if (idMatch) productNameElement.appendChild(document.createTextNode(idMatch[0]));
});
Also note that only some of the elements have an ID number. For example, one of the search results:
BENCHWORLD SONATA 1c Single Adjustable Artist <span class="searchindex-highlight">Piano</span> Bench In Polished Walnut
doesn't have one, so it would be good to check that there's a match first.

Deleting the last Element in a Div [JavaScript]

I'm having a strange problem. I'm trying to make a program that will add and delete div's inside another div called "body". To add divs, I use document.getElementById("body").innerHTML. Adding works fine. However, in the deleting function, I replace the "body" id with a variable with the id of the div that will be deleted. But when I run the code, I get the error "cannot set innerHTML of null". I tried to replace the id variable with a fixed local variable, and it worked fine. I also tried to add quotes to the variable but that didn't work either. Is there any reason why I can't set the id to a changing variable? Thanks.
Here is my code:
var i = 1;
function myFunction() {
var addDiv = document.getElementById("body2");
addDiv.innerHTML += "<div id = '" + i + "'><br><textarea id = '1' > foo < /textarea></div > ";
i++;
}
function myFunction2() {
var deleteDiv = document.getElementById(i);
deleteDiv.innerHTML = "";
i--;
}
<div id="body2">
<div id="0">
<textarea id="text">lol</textarea><button onclick="myFunction()">Add</button>
<button onclick="myFunction2()">Delete</button>
</div>
</div>
you are incrementing i after adding a div so you must use i-1 while deleting to get correct id.
var i = 1;
function myFunction() {
var addDiv = document.getElementById("body2");
addDiv.innerHTML += "<div id = '"+i+"'><br><textarea id = '1'>foo</textarea></div>";
i++;
}
function myFunction2() {
var deleteDiv = document.getElementById(i-1);
deleteDiv.remove();
i--;
}
<div id = "body2">
<div id = "0">
<textarea id = "text">lol</textarea><button onclick =
"myFunction()">Add</button>
<button onclick = "myFunction2()">Delete</button>
</div>
</div>
To remove last child, you can even use CSS selector last-child. You should also add specific class to newly added divs as you would want to remove only newly added divs.
This will also remove dependency of i.
As an addon, you can also use document.createElement + Node.appendChild instead of setting innerHTML. .innerHTMl will be expensive for highly nested structure.
function myFunction() {
document.getElementById("body2").appendChild(getDiv());
}
function getDiv(i){
var div = document.createElement('div');
div.classList.add('inner')
var ta = document.createElement('textarea');
ta.textContent = 'foo';
div.appendChild(ta);
return div;
}
function myFunction2() {
var div = document.querySelector('#body2 div.inner:last-child')
div && div.remove()
}
<div id="body2">
<div id="0">
<textarea id="text">lol</textarea><button onclick="myFunction()">Add</button>
<button onclick="myFunction2()">Delete</button>
</div>
</div>
You can refer "innerHTML += ..." vs "appendChild(txtNode)" for more information.

Set a dynamic variable?

This may be extremely simple, but my brain's just not grasping this for some reason.
I'm making a Tic-Tac-Toe/Hollywood Squares game, and all the square variables are set to null
square0Value = "";
square1Value = "";
etc.
When you click on one of the squares, it launches a bootstrap modal with a closeup of the square and two buttons, X and O, and passes all of the data from the grid square into the modal, so that I only need one modal markup block.
When you click the X or the O, I need it to dynamically set the square variable, that is associated with the modal to either X or O, once the modal is closed, another javascript function checks all the variables to see if there's a 3-in-a-row occurrence, and displays a console.log that X or O has won the game. But I'm not writing this correctly, and none of the square variables are being set.
Here's my code:
HTML:
<button class="squareButton" data-toggle="modal" data-target="#squareModal" data-square="" data-nameplate="" data-xo="" data-id="square0">
<div class="xo"></div>
<div class="nameplate"></div>
<div class="desk"></div>
</button>
(I have this replicated 9 times.)
[Modal markup]
<div class="modal fade-scale" id="squareModal" tabindex="-1" role="dialog" aria-labelledby="squareModal">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-body">
<div class="square">
<div class="squareModal">
<div class="xo"></div>
<div class="nameplate"></div>
<div class="desk"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" id="modalX" class="btn btn-default btn-xo" data-dismiss="modal">X</button>
<button type="button" id="modalO" class="btn btn-default btn-xo" data-dismiss="modal">O</button>
</div>
</div>
</div>
</div>
JS:
I have an array built that populates each square with a celebrity/personality, and an document.ready function that shuffles the array populates the squares:
$(document).ready(function(){
shuffle(squares);
for (i = 0; i < 9; i++) {
$("#square"+i).find('.squareButton').addClass(squares[i][0]);
$("#square"+i).find('.squareButton').data("square", squares[i][0]);
*// the [0] block of the array is a class filler*
$("#square"+i).find('.squareButton').data("nameplate", squares[i][1]);
*// the [1] block of the array is the celebrity's name*
$("#square"+i).find('.nameplate').html(squares[i][1]);
}
});
var square0Value = "";
var square1Value = "";
var square2Value = "";
var square3Value = "";
var square4Value = "";
var square5Value = "";
var square6Value = "";
var square7Value = "";
var square8Value = "";
var squareClass = "";
var squareName = "";
var squareXO = "";
var squareId = "";
$('#squareModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
squareClass = button.data('square');
squareName = button.data('nameplate');
squareXO = button.data('xo');
squareId = button.data('id');
console.log(squareId);
var modal = $(this)
modal.find('.modal-body').addClass(squareClass);
modal.find('.modal-body .nameplate').html(squareName);
modal.find('.modal-body .xo').html(squareXO);
})
$('#squareModal').on('hidden.bs.modal', function (event) {
var modal = $(this);
modal.find('.modal-body').removeClass(squareClass);
checkWin();
});
function clickX(){
$(eval(squareId+'Value')).val("X");
$(eval(squareId)).addClass('selected');
$(eval(squareId)).find(".squareButton").data("xo", "X");
$(eval(squareId)).find(".xo").html("X");
}
function clickO(){
$(eval(squareId)).addClass('selected');
$(eval(squareId+'Value')).val("O");
$(eval(squareId)).find(".squareButton").data("xo", "O");
$(eval(squareId)).find(".xo").html("O");
}
$("#modalX").on("click", function(e){
e.preventDefault();
clickX();
});
$("#modalO").on("click", function(e){
e.preventDefault();
clickO();
});
function checkWin() {
for (i = 0; i < 9; i++) {
console.log("Square"+i+": " + eval('square'+[i]+'Value'));
}
checkWinX();
checkWinO();
if (square0Value != "" && square1Value != "" && square2Value != "" && square3Value != "" && square4Value != "" && square5Value != "" && square6Value != "" && square7Value != "" && square8Value != ""){
checkBoardFull();
}
}
The issue lies in the clickX() and clickO() functions where I am trying to set the value of the (eval(squareId+'Value')) as the X or the O value that is needed. I want to make this as dynamic as possible so don't have to write a function for every modal.
Not sure why you are using eval.
$("#"+squareId).addClass()
may be enough
Here would be my suggestion.
var button = $(event.relatedTarget);
If I am reading this right, that is the button that was clicked that caused the modal to show. If that is the case, then I believe it is a far assumption that only one modal can show at a time. If that is true, then what you can do is make that variable scoped higher, not within that handler.
What does that do for you? Well, if you do that, then you can use that element within your clickX and clickY. For instance...
function clickX(){
button.data('xo', 'X');
button.find('.xo').html('X');
}
I'm unclear what the others are referencing, but if you have the button then you have access to finding it's nested children or looking for parent elements if you need to find them.
So yeah... rebuilding my global Square Values into an array was a MUCH easier way of organizing things... sorry everyone.

Trying to switch image states - from still to animate - using giphy API

So I'm in a bootcamp, bout to begin the 7th week. We have an API assignment, and I chose to use the Giphy API. Okay, I've made the ajax call, have the json object, am displaying gifs with a button click. Once the gifs load, you should be able to make image animate, then upon next click, the image becomes still again. the process should be able to repeat itself ad nauseam. I have it set up the way that makes sense; however, I cant seem to get the urls to change upon click the way it's currently written. I tried .splice url, and using concat to complete updated url; however, I cant seem to figure out how to get the image to revert back to still state upon next click... been working on this for sooooo many hours, will someone please lend me a fresh set of eyes?
here's my js and jquery
// $("#loadTimer, .buttonGallery, #btnGeneratingbtn").hide();
var topics = ["Brandon", "Biggie Smalls", "Dr. Seuss", "Paul McCartney", "John Lennon", "Will Ferrell", "Jimmy Fallon", "Chris Farley", "Dane Cook", "Eminem", "Nas", "Jay-Z", "Rakim", "William Shakespeare","Jim Morrison", "James Maynard", "Serj Tankian"];
var gifcount = 0;
var gifLocation;
var clickCount = 0;
var buttonFactory = function() {
$(".buttonGallery").empty();
for (i = 0; i < topics.length; i++) {
var imAbutton = $("<button>");
imAbutton.addClass("yup");
imAbutton.attr("data-name", topics[i]);
imAbutton.text(topics[i]);
$(".buttonGallery").append(imAbutton);
}};
buttonFactory();
$("#anotherButton").on("click", function(event) {
event.preventDefault();
// This line grabs the input from the textbox
var onemorebutton = $("#user-input").val().trim();
// Adding movie from the textbox to our array
topics.push(onemorebutton);
// Calling renderButtons which handles the processing of our movie array
buttonFactory();
});
$(".yup").on("click", function(){
$("#gif-Gallery").empty();
var searchTermUpdate;
var searchTerm = $(this).attr("data-name");
// removing white space between two-word strings, replacing with a "+" \\
searchTermUpdate = searchTerm.replace(/ +/g, "+");
var queryURL = "http://api.giphy.com/v1/gifs/search?q=" + searchTermUpdate + "&api_key=dc6zaTOxFJmzC&limit=10";
$.ajax({
url: queryURL,
method: 'GET'
}).done(function(response) {
console.log(response);
var results = response.data;
for (var i = 0; i < results.length; i++){
gifcount = gifLocation;
var gifDIV = $("<div class='unit' data-state='still'>");
var pRating = $("<p>").text("Rating: " + results[i].rating);
var gifImg = $("<img id='gifimg' class='col-md-4'>");
gifImg.attr("src", results[i].images.fixed_height_still.url);
gifImg.attr({'data-animate' : results[i].images.fixed_height.url});
gifImg.attr({'data-state' : "still"});
gifImg.attr({'data-still' : results[i].images.fixed_height_still.url});
gifDIV.append(pRating);
gifDIV.append(gifImg);
gifDIV.append(gifLocation);
gifcount++;
// appending gif div to DOM \\
if (results[i].rating !== "g" || "pg" || "pg-13" || "y"){
$("#gif-Gallery").append(gifDIV);
}}
$(".unit").on("click", function(){
var state = $(this).attr('data-state');
if (state === "still") {
$(this).attr("src", $(this).attr("data-animate"));
$(this).attr("data-state", "animate");
} else {
$(this).attr("src", $(this).attr("data-still"));
$(this).attr("data-state", "still");
}
// var imgPath = this.children[1].src;
clickCount++;
var a;
var b;
var c;
var d;
var f;
// if (clickCount % 2 === 0 ){
// for (i=0; i < imgPath.length; i++){
// // locating index of underscore, storing that value in var a\\
// var a = imgPath.indexOf("_");
// // removing all characters to the right of the underscore \\
// var b = imgPath.slice(0, a);
// f = b;
// // setting var c to a string value of .gif \\
// var c = ".gif";
// // combining var b and var c to complete updated image path \\
// var d = b.concat(c);
// }
// setting image url to animated url \\
// $(gifImg).attr("src", d);
// this.children[1].src = d;
});
});}); //
and here is the html if needed :
<body>
<div class="jumbotron">
<div class="container">
<div class="myHeader">
<h1>WordSmiths</h1>
<span class="text-muted" id="jtronText">
<div id="sometimeGone">Sometimes</div><div id="loadTimer">someone opens their mouth and you can't help but to be dazzled with the magic.</div>
</span>
<p class="text-success" id="instructionsOne">
Click a button, see what happens!
</p>
<p class="text-success" id="instructionsTwo">
Now you can create your own buttons!!!
</p>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class="buttonGallery"></div>
<h4>Create buttons, find Gifs!: </h4>
<form id="btnGeneratingForm">
<label for="input">Anything: </label>
<input type="text" id="user-input">
<br>
<input type="submit" id="anotherButton" value="More buttons">
</form>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div id="gif-Gallery"></div>
</div>
</div>
again, thanks in advance!
Robert
I am actually doing the same project! I did it a pretty a similar way. I think the issue may be that you're appending the animated src to the image holder div instead of the child image itself. This line:
$(this).attr("src", $(this).attr("data-animate"));
Is what is giving you trouble I think.
Best of luck with the project, I hope that helps.

Update text on button using JavaScript

Have such button on my page (for showing current total in shopping cart):
<div class="col s2 offset-s4 valign" id="shoppingCart">
<a class="waves-effect waves-teal btn-flat no-margin white-text right" th:inline="text"><i class="material-icons right">shopping_cart</i>[[#{${total}+' руб'}]]</a>
</div>
Written function for updating total number on that button:
function updateShoppingCart(newTotal) {
var $div = $("#shoppingCart");
var $a = $("<a class='waves-effect waves-teal btn-flat no-margin white-text right'></a>");
var $i = $("<i class='material-icons right'>shopping_cart</i>");
var $string = formatDecimal(newTotal,',','.') + " руб";
$a.append($i);
$a.append($string);
$div.children().remove();
$div.append($a);
}
But it does not work. Please help find bug or what I'm doing wrong.
It's a lot more efficient to set $div = $('#shoppingCart');; in the global scope and use that var instead. This way your JS won't search through your entire DOM every time the function is called.
Your stuff doesn't work because your vars are very odd. I believe what you want to achieve is this:
HTML
<a class="waves-effect waves-teal btn-flat no-margin white-text right" th:inline="text">
<i class="material-icons right">shopping_cart</i>
<span>[[#{${total}+' руб'}]]</span>
</a>
JS:
function updateShoppingCart(newTotal) {
var $div = $("#shoppingCart");
var $a = $("a", $div); //select the <a> within the shoppingcart div
var $i = $("i", $div); //select the <i> within the shoppingcart div
var $span = $("span", $div); //select the newly added span
var newPrice = formatDecimal(newTotal,',','.') + " руб";
$span.text(newPrice);
}
I kept the $a and $i in as examples, but I don't see a need for you to use them or completely replace them, since you only want to change the text. By using a span, you can target the price without replacing all the html.
On a sidenote, the $ is generally used to state a var is a jquery object. string is not a jquery object in this scenario, so the $ there is a bit odd.
On a sidenote, if you want to replace html within an element, you should try doing it like so:
function updateShoppingCart(newTotal) {
var $div = $("#shoppingCart");
var newPrice = formatDecimal(newTotal,',','.') + " руб";
//Create the new HTML as a string, not as an element
var newHtml= '<i>Shopping_cart</i>'+newPrice+'';
//empties div beforehand current html, no seperate removal needed.
//Then places the html string within the element
$div .html(newHtml);
}
See working JSFiddle here

Categories