Inject divs with jQuery while keeping word distance from specific elements - javascript

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.

Related

Is there a jQuery UI "anti-sortable" that will let LI's be taken and from a UL, but always specifying order?

I have something partially working where there are two UL's, both having had jQuery UI .sortable() called on it. The user can and potentially should drop LI's from one to the other. I am looking to have the second list really be sortable, but the first list retain a single ordering instead of having a LI from the second list appended at the end if the user clicks on it.
I see one painfully obvious way to do it: keep a JavaScript list of values of LI's, or alternately set a data-index='0' (then, 1, 2, 3, etc.), and then in either case make a single, possibly bottom-up, sweep of the dread bubble sort.
This appears to me something I could straightforwardly get working, but it has an "If you're doing it this way, you're working too low-level" code smell to me. Apart from a bubble sort reference, in a case where I think O(n) really is tolerable, it seems like something where someone who knew jQuery UI could produce a much shorter and clearer implementation.
I've outlined above the hard way of addressing my problem. What easy ways should I consider instead.
After a little more hesitation, I decided to go with the obvious solution, even if it doesn't smell like a usual jQuery optimal solution. I added an ascending integer data- field to the original LI's in the server-side code that generates them, and client-side created a single bubble sort iteration function that should work if all but the last item (re-added items start out last) are in order:
var get_index = function(node)
{
return parseInt(jQuery(node).attr('data-sort-position'));
};
var sort_available = function()
{
var len = jQuery('#available > li').length;
if (len >= 2)
{
var moved = false;
for(var index = 0; index < len; index += 1)
{
if (!moved)
{
if (get_index(jQuery('#available > li')[index]) >
get_index(jQuery('#available > li')[len - 1]))
{
var node = jQuery(
jQuery('#available > li')[len - 1]).detach();
jQuery(node).insertBefore(
jQuery('#available > li')[index]);
moved = true;
}
}
}
}
};
I then added a sort_available() call to the end of the handler that is called when a member of the other, destination list is clicked:
jQuery('#included li').click(function(event)
{
jQuery(event.target).detach();
jQuery('#available').append(event.target);
assign_clicks();
sort_available();
});
Now it seems to be putting items back in their original places.

jQuery .detach / append in if/else statement

I'm having a bit a trouble with creating a category filter for my custom post type taxonomy, I've tried allot of different things in jQuery and I think this should be the best way to get what I want.
Code:
var divs;
$('.category').on('click', function() {
if(divs == 1) {
alert('ayylmao?');
$('section#showcase').append(detached);
divs = 0;
} else {
divs = 1;
var lolxd = $(this).find('a').attr('id');
alert(lolxd);
var detached = $('section#showcase').contents().not("." + lolxd).detach();
}
});
It has been a while ago that I used jQuery but for some reason the var 'detached' wont append to the section showcase.
Would like to hear what I am doing wrong ;_;
detached is declared within your click handler function. That means it's re-created every time the function runs.
Contrast that to your use of divs which is declared outside the function and thus persists across function calls.
There are many ways to solve this, but the simplest would be to follow the same pattern that you have for divs and have code that looks like this:
var divs;
var detached;
$('.category').on('click', function() {
if(divs == 1) {
alert('ayylmao?');
$('section#showcase').append(detached);
divs = 0;
} else {
divs = 1;
var lolxd = $(this).find('a').attr('id');
alert(lolxd);
detached = $('section#showcase').contents().not("." + lolxd).detach();
}
});
Speaking more generally, it sounds like when you detach a showcase, you want to keep it around so you can add it back later. Depending on the makeup of your program, it might be more useful to just change the display of that item to none. However, this does change the semantics if you had multiple things you could append to your showcase.

Insert a span around nth-word within a div

I'm designing a rudimentary spell checker of sorts. Suppose I have a div with the following content:
<div>This is some text with xyz and other text</div>
My spell checker correctly identifies the div (returning a jQuery object entitled current_object) and an index for the word (in the case of the example, 5 (due to starting at zero)).
What I need to do now, is surround this word with a span e.g.
<span class="spelling-error">xyz</span>
Leaving me with the final structure like this:
<div>
This is some text with
<span class="spelling-error">xyz</span>
and other text
</div>
However, I need to do this without altering the existing user selection / moving the caret / invoking methods that do so e.g.
window.getSelection().getRangeAt(0).cloneRange().surroundContents();
In other words, if the user is working on the 4th div in the contenteditable document, my code would identify issues in the other divs (1st - 3rd) while not removing focus from the 4th div.
Many thanks!
You've tagged this post as jQuery but I don't think it's particularly necessary to use it. I've written you an example.
https://jsfiddle.net/so0jrj2b/2/
// Redefine the innerHTML for our spellcheck target
spellcheck.innerHTML = (function(text)
{
// We're using an IIFE here to keep namespaces tidy.
// words is each word in the sentence split apart by text
var words = text.split(" ");
// newWords is our array of words after spellchecking.
var newWords = new Array;
// Loop through the sentences.
for (var i = 0; i < words.length; ++i)
{
// Pull the word from our array.
var word = words[i];
if (i === 5) // spellcheck logic here.
{
// Push this content to the array.
newWords.push("<span class=\"mistake\">" + word + "</span>");
}
else
{
// Push the word back to the array.
newWords.push(word);
}
}
// Return the rejoined text block.
return newWords.join(" ");
})(spellcheck.innerHTML);
Worth noting my usage of an IIFE her can be easily reproduced by moving that logic to its own function declaration to make better use of it.
Be aware you also need to account for punctuation in your spellchecking instances.

Remove all elements before a specified ID attribute using pure javascript

I'm using a web service to query a ticketing interface and pull back all support tickets that are open at any point in time, displaying them in a nice neat table within an iframe as part of our web application. My application is written in PL/SQL however I use this to construct my HTML pages and the content that is displayed within my iframe. My main issue is that because the tickets are made up of Email content, there is a lot of useless rubbish (mostly tags) and irrelevant text that is included at the top of the Email content which causes my interface to look trashy.
Within my code I am wrapping the main areas of content that I require in a table with an ID of "ticketsTable" and would like to remove any elements or content within the iframe only that occurs before this tickets table.
Usually I would use jQuery for this task:
$("#earliercontent").nextUntil("#ticketsTable").andSelf().remove();
however, our system is built using ExtJS so we wish to avoid any potential conflict the jQuery library could cause with this environment. I therefore need a way to process and loop through the contents of the iframe, removing all elements and text that occur before the ticketTable id therefore eradicating all of the unnecessary content.
I've already tried this:
var last = null;
var curr = $('#page1');
while (curr.attr('id') != 'ticketsTable') {
if (last != null) {
last.remove();
}
last = curr;
curr = curr.next();
}
if (last != null) {
last.remove();
}
Source: jQuery remove all elements until id='whatever' found
however, as the Email content is variable, there is no specific ID tag I can specify for the script to start at. Therefore, is there an alternative method I can use (without a library, just pure JavaScript) to remove all elements and content before the ticketsTable?
I must stress that the contents of the ticketsTable must remain intact. It is only the content that occur before this within the body that I wish to remove.
http://jsfiddle.net/yJrb7/
var parent = document.getElementById("parent");
var stopID = "stop";
var length = parent.childNodes.length, j = 0;
for(var i=0; i < length; i++){
if(parent.childNodes[j].id != stopID){
parent.removeChild(parent.childNodes[j]);
}
else{
j++;
}
}
When you remove a child node, the array is shifted, so if for example you need to remove two first nodes, then you would call removeChild for index 0 two times.

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.

Categories