Javascript Replace function not working on first instance - javascript

I am currently trying to make a list, using javascript and jquery that returns a string of keywords as shown here: keyword+keyword+keyword+keyword
I am attempting to do this by adding a class 'active' when a keyword is clicked as shown in the code below, then checking whether it is active, if it is, add it to a list with an additional + sign (except for the first element, limited by the counter), however whenever I click on an item 2 times consecutively, it is successfully added to the list however is not deleted from it, yet the counter is added and subtracted per usual, and the console log reports the class changing.
The code is below;
$("ul.cats li, ul.colours li ").click(function(){
$(this).toggleClass( 'active');
});
var taglist = '';
var tagcounter = 1;
$("ul.cats li.tag").click(function(){
var tag = $(this).attr("list-data");
if ($(this).hasClass('active')) {
if (tagcounter <= 0){
taglist += '+';
}
taglist += tag;
tagcounter -= 1;
console.log('Active');
} else {
var tag2 = '+' + tag;
taglist = taglist.replace(tag2," ");
tagcounter += 1;
console.log('inactive');
}
console.log(taglist, 'Taglist', tagcounter);
});
From this I believe I should get a list of keyword1+keyword2+keyword3 however whenever an item is clicked twice in succession, (keyword1 is rendered active and inactive consecutively) the keyword is not deleted yet the counter is incremented and the class is rendered inactive.
Using an example keyword of 'light', this is what is returned by the console;
[Log] Active
[Log] Light Taglist 0
[Log] inactive
[Log] Light Taglist 1 (wordpress, line 237)
This shows that the class checks and if statement is functioning properly yet the replace function is not.
Please can someone provide a solution to this, I will be eternally grateful!

The problem is with the check of tagcounter <= 0.
When you are checking that for the first time, tagcounter is still 1. You decrement it later. Hence the "+" never gets appended. Hence when you are trying to remove "+Light" it doesn't work because the "+" is not there in the taglist.
Move the decrement code tagcounter -=1 above the tagcounter check.
tagcounter -= 1;
if (tagcounter <= 0){
taglist += '+';
}
taglist += tag;
It works fine. Here is a working demo http://jsfiddle.net/gqgL3dgk/
This would append a "+" in the beginning of your taglist. You can get rid of that extra "+" sign by using substring method.
taglist = taglist.substring(1); //includes all characters except the 0th
Recommendation
Since you have to maintain a list, use a data structure that is more suited for a list (i.e. an Array) and convert that to a "+" separated string when you need it.
You can keep pushing/pulling tags from the array and convert it to a "+" separated string using the join() method.
Working fiddle here http://jsfiddle.net/gqgL3dgk/1/
This will remove your dependency on tagcounter (because the array length can get you that). And adding and removing elements is super simple.

The first argument of .replace() needs to be a regular expression. You can generate it from a string by using new RegExp():
taglist = taglist.replace(new RegExp(tag2)," ");
Edit: Ooops - a string is valid as a first agrument. It is not treated like a regular expression, but in this example it doesn't need to be.

Related

How do you compare JavaScript innerHTML return values, when there's a special character involved?

I have a web page with a form on it. The "submit" button is supposed to remain deactivated until the user fills in all the necessary fields. When they fill in a field, a checkmark appears next to it. When all the checkmarks are there, we're good to go.
A checkmark might be set by code like this:
if (whatever) checkLocation.innerHTML = CHECKMARK;
Here's the code I'm using to do the final check. It just loops through all the locations where there may be checkmarks. If it finds a location without a mark, it disables the submit button and leaves. If it gets through them all, it activates the button and returns true.
function checkSubmitButton() {
var button = document.getElementById(SUBMIT_BUTTON);
for (var i=0; i<CHECK_LOCATIONS.length; i++) { // should be for-each, but JS support is wonky
var element = document.getElementById(CHECK_LOCATIONS[i]);
console.log(CHECK_LOCATIONS[i] +": " +element.innerHTML);
// if found unchecked box, deactivate & leave
if (element.innerHTML != CHECKMARK) {
button.disabled = true;
return false;
}
}
// all true--activate!
console.log("ACTIVATING BUTTON!");
button.disabled = false;
return true;
}
Here's the problem: this works so long as the const CHECKMARK contains something simple, like "X". But specs call for a special HTML character to be used: in this case ✓, or ✓. When I do the comparison (in the if line) it ends up comparing the string "✓" to the string "✓". Since these two are not equal, it doesn't recognize a valid checkmark and the button never activates. How can I compare the contents of the HTML element my constant? (And hopefully make the code work even if down the road somebody replaces the checkmark with something else.)
Thanks.
There is no problem with the check character and it behaves exactly like the X character. The problem is, that your html have the checkmark character stored as html entity in hex string. If you compare checkmark to checkmark it works just fine: https://jsfiddle.net/m7yoh026/
What you can do in your case is to make sure the CHECKMARK variable is the actuall checkmark character, not the html entity.
Other option is to decode the html entity: https://jsfiddle.net/m7yoh026/3/
var CHECKMARK = '✓'
var decoded_checkmark = $('<textarea />').html(CHECKMARK).text();
console.log($('div')[0].innerHTML)
if ($('div')[0].innerHTML == decoded_checkmark) {
$('body').append('checkmark recognized<br>')
}
You can convert a character to its HTML entity equivalent like so:
var encoded = raw.replace(/[\u00A0-\u9999<>\&]/gim, function(i) {
return '&#'+i.charCodeAt(0)+';';
});
Well, here's what I ended up doing: I made a function called encodeHtml() that takes a character or string, writes it to a brand new div, and then returns what's contained in that div:
function encodeHtml(character) {
var element = document.createElement("div");
element.innerHTML = character;
return element.innerHTML;
}
Then I can compare to what it returns, since it automatically changes "✓" to "✓", and will work with any unforeseen changes to that character. It's a bit of a kludge, but it works. (It's still not clear to me why JavaScript does this automatic conversion...but there are many design choices in which JavaScript mystifies me, so there you go.)
Thanks all for the help.

Inject divs with jQuery while keeping word distance from specific elements

I need to inject some divs (that contain links to other articles in the site) in the content of a page. The rules in order to inject these divs are pretty tricky:
They need to be injected always after a paragraph.
There needs to be a minimum amount of words before the first injected div.
There needs to be a minimum amount of words before the previous injected div and the one that is being inserted right now.
There needs to be a minimum amount of words between the currently injected div and some specific elements in the content.
I have created a function that aspires to do the things mentioned above and looks like this:
function elementProximityCheck(paragraphOffset, wordSpace, elSelector) {
var elementsToCheck = ['figure', 'blockquote'];
var wordCounter, paragraphOffsetMin, paragraphOffsetMax;
wordCounter = 0;
paragraphOffsetMin = paragraphOffset-1;
do {
wordCounter += numOfWords(paragraphOffsetMin, elSelector);
paragraphOffsetMin -= 1;
} while (wordCounter < wordSpace);
wordCounter = 0;
paragraphOffsetMax = paragraphOffset;
do {
wordCounter += numOfWords(paragraphOffsetMax, elSelector);
paragraphOffsetMax += 1;
} while (wordCounter < wordSpace);
$(elSelector).slice(paragraphOffsetMin, paragraphOffsetMax + 1).each(function () {
for (var elementIndex = 0; elementIndex < elementsToCheck.length; elementIndex++) {
if ($(this).is(elementsToCheck[elementIndex])) {
paragraphOffset = elementProximityCheck(paragraphOffsetMax, wordSpace, elSelector);
return false;
}
}
});
return paragraphOffset;
}
This function is called after I have identified the first paragraph that meets the 2nd condition above.
The main idea is that I search backward and forward for an element of the elementsToCheck array and if such an element is found, the function calls itself recursively with a new paragraph index (which is the paragraph that I used as a bound in the previous execution).
The problem is that this thing does not work and I can't really figure out why. Any insights (or alternative solutions) will be highly appreciated.
Edit:
Below you can find a JSFiddle implementation of my code along with the extra function that I'm using to count the words.
JSFiddle Example
Edit 2: Using the JSFiddle, the paragraphIndex that I'm getting back is always the same paragraph that I provide. In theory, the code should move/increase the paragraph index when a figure or blockquote is found in the "wordSpace proximity" of the current paragraph element.
For example, if the wordSpace variable is 60, then assuming that we start looking from the 3rd paragraph (paragraphIndex = 2), in the first execution of the function, the paragraphOffsetMin should be 0, paragraphOffsetMax should be 3, and then it should call itself with 3 as the paragraphIndex. In the recursive call, the paragraphOffsetMin should be 2 and paragraphOffsetMax should be 4 and this is what should be returned in the end.
What I'm basically trying to do is inject the elements using specific "word distances" between them and also "avoiding the hurdles of blockquotes/figures. I suspect that there are better algorithms than the one that I provided though.

Splitting a long phrase into an array

I need to take the phrase
It’s that time of year when you clean out your closets, dust off shelves, and spruce up your floors. Once you’ve taken care of the dust and dirt, what about some digital cleaning? Going through all your files and computers may seem like a daunting task, but we found ways to make the process fairly painless.
and upon pressing a button
split it into an array
iterate over that array at each step
Build SPAN elements as you go, along with the attributes
Add the SPAN elements to the original DIV
Add a click handler to the SPAN elements, or to the DIV, which causes the style on the SPAN to change on mouseover.
So far I had
function splitString(stringToSplit, separator) {
var arrayOfStrings = stringToSplit.split(separator);
print('The original string is: "' + stringToSplit + '"');
print('The separator is: "' + separator + '"');
print("The array has " + arrayOfStrings.length + " elements: ");
for (var i=0; i < arrayOfStrings.length; i++)
print(arrayOfStrings[i] + " / ");
}
var space = " ";
var comma = ",";
splitString(tempestString, space);
splitString(tempestString);
splitString(monthString, comma);
for (var i=0; i < myArray.length; i++)
{
}
var yourSpan = document.createElement('span');
yourSpan.innerHTML = "Hello";
var yourDiv = document.getElementById('divId');
yourDiv.appendChild(yourSpan);
yourSpan.onmouseover = function () {
alert("On MouseOver");
}
and for html I have
The DIV that will serve as your input (and output) is here, with
id="transcriptText":</p>
<div id="transcriptText"> It’s that time of year when you clean out your
closets, dust off shelves, and spruce up your floors. Once you’ve taken
care of the dust and dirt, what about some digital cleaning? Going
through all your files and computers may seem like a daunting task, but
we found ways to make the process fairly painless.</div>
<br>
<div id="divideTranscript" class="button"> Transform the
Transcript! </div>
Any help on how to move one? I have been stuck for quite some time
Well, first off this looks like homework.
That said, I'll try to help without giving you the actual code, since we're not supposed to give actual working solutions to homework. You're splitting the string too many times (once is all that's needed based on the instructions you gave) and you have to actually store the result of the split call somewhere that your other code can use it.
Your instructions say to add attributes to the span, but not which attributes nor what their contents should be.
Your function should follow the instructions:
1) Split the string. Since it doesn't specify on what, I'd assume words. So split it on spaces only and leave the punctuation where it is.
2) with the array of words returned from the split() function, iterate over it like you attempt to, but inside the braces that scope the loop is where you want to concatenate the <span> starting and ending tags around the original word.
3) use the document.createElement() to make that current span into a DOM element. Attach the mouseover and click handlers to it, then appendChild() it to the div.
add the handler to your button to call the above function.
Note that it's possibly more efficient to use the innerHTML() function to insert all the spans at once, but then you have to loop again to add the hover/click handlers.

How to reference an array in a function argument

I have a series of arrays that contain words I want to use as text in various HTML divs (there are about 35 of these, I included only a few for brevity).
var bodyplan = ['Anguilliform', 'Compressiform', 'Depressiform', 'Filiform', 'Fusiform', 'Globiform', 'Sagittiform', 'Taeniform'];
var mouthposition = ["Inferior", "Jawless", "Subterminal", "Superior", "Terminal"];
var barbels = ['1', '2', '4 or more'];
var caudalshape = ['Continuous', 'Emarginate', 'Forked', 'Lunate', 'Rounded', 'Truncate'];
I have a switch function that is supposed to change the text based on user selections:
switch(n){
case 1:
changelabels(bodyplan, 8);
break;
case 2:
changelabels(mouthposition, 5);
break;
case 3:
changelabels(barbels, 3);
break;
case 4:
changelabels(caudalshape, 6);
break;
case 5:
changelabels(dorsalspines, 8);
break;
default:
alert("handquestsel error")}};
Finally, I have the function which I would like to make the changes (except it doesn't):
function changelabels(opt1,opt2){
var i = opt2;
var im = opt2 - 1;
var c = 1;
var index = 0;
while (i>=c){
var oldlbl = document.getElementById("rb" + c + "lbl");
var newlbla = opt1.slice(im,i);
var newlblb = opt1.toString();
oldlbl.innerHTML = newlblb;
c = c + 1
index = index + 1
}};
I know the code for my function is just plain wrong at this point, but I have altered it so many times that I'm not sure what's going on anymore. At one point I did have the function able to change the text, but it did so incorrectly (it parsed the name of the array, not extracted a value from the array as I wished). Please help. I know I am overlooking some fundamental concepts here, but am not sure which ones. I've lost count of the hours I've spent trying to figure this out. It's seems like it should be so simple, yet in all my chaotic attempts to make it work, I have yet to stumble on an answer.
EDIT: I want my switch statement to call the function and pass to the function, the appropriate array from which to pull the labels from. The purpose of the app is to help a user learn to identify fish. When the user makes selections on the page, a series of pictures will be shown for various character states with an accompanying label describing the state. For example, when the user selects Mouth Position a series of divs will show the different mouth positions that fish have and have a label below the picture to tell the user what that certain character state is called. I can get the pictures to change just fine, but I am having a hell of a time with the labels.
Why not just something along the lines of:
document.getElementById("bodyplan_label").innerHTML = bodyplan[bodyplan_index];
You seem trying to put everything in really abstract data structures, I see no reason to. Just keep it simple.
Also bodyplan has only 8 elements, so bodyplan[8] will give you an out of bounds exception because arrays start at 0 as is common in all modern programming languages.
If I'm reading your requirement and code correctly, in your switch statement you are passing both a reference to the appropriate array and that array's expected length - you don't need the second parameter because all JavaScript arrays have a .length property.
You don't want to use .slice() to get the individual values out of the array, because that returns a new array copied out of the original - just use arrayVariable[index] to get the individual item at index.
So, putting that together try something like this (with your existing array definitions):
switch(n){
case 1:
changelabels(bodyplan);
break;
case 2:
changelabels(mouthposition);
// etc.
}
function changelabels(data) {
var i,
lbl;
for (i = 0; i < data.length; i++) {
lbl = document.getElementById("rb" + (i+1) + "lbl");
lbl.innerHTML = data[i];
}
}
Notice how much simpler that is than your code? I'm assuming here the elements you are updating have an id in the format "rb1lbl", "rb2lbl", etc, with numbering starting at 1: I'm getting those ids using (i+1) because JavaScript array indexes start at zero. Note also that you don't even need the lbl variable: you could just say document.getElementById("rb" + (i+1) + "lbl").innerHTML = data[i] - however I've left it in so that we have something to expand on below...
Within your function you seem to be changing the labels on a set of elements (radio button labels?), one per value in the array, but you stop when you run out of array items which means any leftover elements will still hold the values from the previous selection (e.g., if the previous selection was "bodyplan" with 8 options and you change to "mouthposition" with only 5 - you probably should hide the 3 leftover elements that would otherwise continue to display the last few "bodyplan" items. One way to do that is instead of setting your loop up based on the array length you could loop over the elements, and if the current element has an index beyond the end of the array hide it, something like this:
function changelabels(data) {
var i,
lbl,
elementCount = 20; // or whatever your element count is
for (i = 0; i < elementCount; i++) {
lbl = document.getElementById("rb" + (i+1) + "lbl");
if (i < data.length) {
lbl.innerHTML = data[i];
lbl.style.display = "";
} else {
lbl.innerHTML = "";
lbl.style.display = "none";
}
}
}
If these elements are labels for radio buttons (just a guess based on the ids) then you'd also want to hide or show the corresponding radio buttons, but I hope you can figure out how to add a couple of lines to the above to do that.
(As mentioned above, be careful about having element ids count up from 1 when the array indexes start at 0.)
If the above doesn't work please post (at least some of) the relevant HTML - obviously I've just had to guess at what it might be like.
SOLUTION: Changed the scope of the array variables to local by moving them into the function where they are used, instead of having them as global variables at the top of the page. I don't understand as I was following every rule of variable declaration. But for some unknown reason, global variables in javascript are abhorrent.
Solution Edit: Found an error in declaring my global variables. This may have been the source of my problem of why I could not access them. But it is a non-issue at this point since I corrected my code.
I don't understand what your trying to achieve exactly with your code. But to pass a variable (in this case an array) by reference you just have to add "&" before the variable.
function the_name(&$var_by_ref, $var_by_value) {
// Here if you modify $var_by_ref this will change the variable passed to the function.
}
More: http://php.net/manual/en/language.references.pass.php
Hope that helps.

How can I get the JQuery to return a valid img src?

So, I have images in a div that share the same class and one of them is selected at a time for a single view of that image. In this single view, I have arrows that I want to use to cycle between the images. The images sharing the same class have sequential alt values.
Right now, I can get the preceding alt value and set the src of the image in the single view to the source of that image. But, I can't seem to get the same effect with the following image.
$("#leftButton").click(function(){
var current= $('img[id="selected"]');
var altVal=current.attr("alt").valueOf()-1;
if(altVal>=0)
{
var next= $('img[alt='+altVal+']');
current.attr("id", "");
next.attr("id", "selected");
$("#embiggenImage").attr("src", next.attr("src"));
}
}
);
$("#rightButton").click(function(){
var gal=$('img[class="galleryImage"]');
alert(gal.length);
var current= $('img[id="selected"]');
var altVal=current.attr("alt").valueOf()+1;
if(altVal<gal.length)
{
alert("inside");
var next= $('img[alt='+altVal+']');
current.attr("id", "");
next.attr("id", "selected");
$("#embiggenImage").attr("src", next.attr("src"));
}
}
);
As you can see, the code for changing the sources is exactly the same, and it reaches those two alerts fine, but returns a null source.
Edit: It seems that in the leftButton click function, alt is being properly decremented, but in the rightButton click function, when I am trying to increment alt, it instead, is appending the '1', such that the final value of altVal is '01'. This does not make any sense to me..
The problem is in the difference between these two lines:
var altVal=current.attr("alt").valueOf()-1;
var altVal=current.attr("alt").valueOf()+1;
- only has one meaning: subtract. If you call it on a string, it will be converted into a number before the operation.
+ has two meanings: add and concatenate. If you call it on a number, it adds. If you call it on a string, it concatenates. attr returns a string (as does valueOf -- I'm not sure what the point of that is), so +1 means "put add 1 to the end of the string".
You need to convert the value to a number instead. I'd use the unary + operator:
var altVal = +current.attr('alt') + 1;
If the final value in the rightButton click event for "alt" is "01", try using parseInt() before adding to it, like so:
parseInt(current.attr("alt").valueOf())+1;

Categories