Trouble with jquery's append and prepend - javascript

I use the following code to check whether or not a certain div exists. And if it doesn't it adds it to the dom in the proper place.
if (!($("#col"+lastSlide).length))
{
$('#schDisplay').prepend('<div id="col' + (lastSlide) + '"></div>');
}
My problem is that when this if statement checks again for a div that had already once been appended/prepended, it doesn't see the div that I added. So it will continue to add the div that was already prepended/appended.
Is there any way this issue can be fixed?
more code:
//fillin added column
function fillElement(colnum)
{
var URL = 'schedule.php';
$("#col"+colnum).text("Loading...").show();
$.post(URL,{fecolnum: colnum},
function (data)
{
$("#col"+colnum).html(data).show();
});
}
//...irrelevant code...
// Create event listeners for .arrows clicks
$('.arrows')
.bind('click', function(){
numberOfSlides++;
// Determine new position
if ($(this).attr('id')=='arrow_right')
{
lastSlide++;
currentPosition++;
if (!($("#col"+lastSlide).length))
{
$('#schDisplay').append('<div id="col' + lastSlide + '" class="schColumn" style="float: left; width: ' +slideWidth+ 'px"></div>');
fillElement(lastSlide);
}
}
else
{
lastSlide--;
currentPosition--;
if (!($("#col"+lastSlide-2).length))
{
$('#schDisplay').prepend('<div id="col' + (lastSlide-2) + '" class="schColumn" style="float: left; width: ' +slideWidth+ 'px"></div>');
fillElement(lastSlide-2);
}
}

I don't have much context to go off of, but checkout this jsfiddle I made from what you gave me.
Notice the line where I've made a comment that you can comment out the increment statement. Basically, this is how you can test what happens when "lastSlide" is the same value. (versus if "lastSlide" is indeed a different value).
You might just try debugging your actual code and make sure that lastSlide has the value you actually expect.

If I understand your question correctly, you're saying that it keeps adding the div if you do the same thing again. However, I can't reproduce this. The following code shows one div. According to you, it would show two divs.
lastSlide = 1;
if (!($("#col"+lastSlide).length))
{
$('#schDisplay').prepend('<div id="col' + (lastSlide) + '"></div>');
}
if (!($("#col"+lastSlide).length))
{
$('#schDisplay').prepend('<div id="col' + (lastSlide) + '"></div>');
}
If I didn't miss something, your problem must lie elsewhere.

This will work:
if (!($("#col"+lastSlide).length)) {
var newdiv = $('<div id="col'+lastSlide+'"/>');
$('#schDisplay').prepend(newdiv);
}

It keeps adding because your if condition is always true.
I haven't tested this though...
Can you try something like this to see if it helps?
var len = 0;
len = $("#col"+lastSlide).length;
if (len < 1)
{
$('#schDisplay').prepend('<div id="col' + (lastSlide) + '"></div>');
}

You can use jQuery filters to remove the selected div if it has already been prepended to. This should work for your particular scenario:
$('#schDisplay').filter(':not(:has(div[id^=col]))')
This says "select the div with id 'schDisplay' and then remove it from the selection if it has any descendent divs with ids start with 'col'".
In your example, you would chain on your prepend statement to this code like so (no need for an if statement):
$('#schDisplay').filter(':not(:has(div[id^=col]))').prepend('<div id="col' + (lastSlide) + '"></div>');
A special note: I believe that the :has selector is a jQuery content filter selector, meaning that it will not work without jQuery. You are clearly using jQuery, but readers with different implementations should keep this in mind.

Related

jQuery dynamic checkbox position, merging examples found

When dynamically adding checkboxes in jquery, the position is incorrect:
I have an example (taken from stakoverflow) in JSFiddle of adding checkboxes dynamically.
I also have a link which claims they got it right to add the position,
<!-- language: lang-js -->
var $myCheckboxLabel = $("label[for='myCheckbox']");
$myCheckboxLabel.position({my:"left", at:"right", of:$myButton });
but this does not tie up at all with the example as mentioned:
for (var i = 0; i < 40; i++) {
$("fieldset").append('<input type="checkbox" name="' + name + '" id="id' + i + '"><label for="id' + i + '">' + name + ' ' + i + '</label>');
}
Can someone please help met to set the position of the checkbox that gets added in the loop, so it does not show next to each other, but in the next row.
Your Javascript is fine for what you're trying to do - it'll generate the correct HTML. The problem is that they're being displayed "inline" which means that they all go side-by-side, as many on one line as it can fit, then it'll wrap down a line.
What you want is for them to be displayed as a "block" element which means it has its own line, and the next element will have to go underneath it. You can just use CSS to make them do this:
fieldset input[type=checkbox] { /* this can be more specific if you need it to be */
display: block;
}
Here's a fiddle demo'ing this.

Constructing HTML elements with readability and maintenance in mind

When iterating over some table cell data, I construct an array of whatever's found. The first iteration simply wraps the found text in some <span> tags, whilst the subsequent ones add a bunch of other styles, as below:
var array = $('table').find('th').map(function(i){
if (i===0){
// This bit's OK
return '<span>' + $(this).text() + '</span>';
} else {
// This just looks horrible
return '<span style="width:' + $(this).outerWidth() + 'px; display:inline-block; position:absolute; right:' + w[i-1] + 'px">' + $(this).text() + '</span>';
}
}).get();
It all works fine, but it's hideous - and I think I've seen a much more elegant way of constructing HTML elements, with styles, somewhere before.
Can anyone suggest a more "maintenance-friendly" way of writing the else statement?
EDIT: Using CSS classes isn't really a solution, as the code applies values based on other calculations.
As was already suggested in the comments, consider storing the values that are used on all elements in a CSS class, I'll choose .somethingfor this example.
.something {
position: absolute;
display: inline-block;
}
Next, in the jQuery, you can store a copy of your span element in a variable as you're going to use it in both cases. In the else block you can then simply apply the class and add the individual styles.
EDIT: You can simplify the code even more. You'll return the span whatever happens so you only have to check if i is not equal to 0.
var array = $('table').find('th').map(function (i){
var span = $('<span>' + $(this).text() + '</span>');
if (i !== 0) {
span.addClass('something').css({
width: $(this).outerWidth() + 'px',
right: w[i-1] + 'px'
});
}
return span;
}).get();
How about something more like this?
var array = $('table').find('th').map(function(i){
var element = $("<span></span>").text($(this).text());
return (i === 0) ? element : element.css({"width": $(this).outerWidth() + "px"
, "display": "inline-block"
, "position": "absolute"
, "right": w[i-1] + "px"});
}).get();

jQuery 'not' function giving spurious results

Made a fiddle: http://jsfiddle.net/n6ub3/
I'm aware that the code has a LOT of repeating in it, its on the list to refactor once functionality is correct.
The behaviour i'm trying to achieve is if there is no selectedTab on page load, set the first tab in each group to selectedTab. If there is a selectedTab present, then use this as the default shown div.
However, as you can see from the fiddle its not working as planned!
If anyone has any ideas how to refactor this code down that'd be great also!
Change
if($('.tabs1 .tabTrigger:not(.selectedTab)')){
$('.tabs1 .tabTrigger:first').addClass('selectedTab');
}
to
if ( !$('.tabs1 .tabTrigger.selectedTab').length ) {
$('.tabs1 .tabTrigger:first').addClass('selectedTab');
}
Demo at http://jsfiddle.net/n6ub3/1/
They way you are doing it (the first code part) you are adding the .selectedTab class if there is at least one of the tabs in that group that is not selected at start .. (that means always)
Update
For a shortened version look at http://jsfiddle.net/n6ub3/7/
Your selector are doing exactly what you're writing them for.
$('.tabs3 .tabTrigger:not(.selectedTab)') is true has long as there is at least one tab that has not the selected tab (so always true in your test case).
So you should change the logic to !$('.tabs3 .tabTrigger.selectedTab').length which is true only if there are no selectedTab
WORKING DEMO with simplified code
$('.tabContent').hide();
$('.tabs').each(function(){
var search = $(this).find('div.selectedTab').length;
if( search === 0){
$(this).find('.tabTrigger').eq(0).addClass('selectedTab')
}
var selectedIndex = $(this).find('.selectedTab').index();
$(this).find('.tabContent').eq(selectedIndex).show();
});
$('.tabTrigger').click(function(){
var ind = $(this).index();
$(this).addClass('selectedTab').siblings().removeClass('selectedTab');
$(this).parent().find('.tabContent').eq(ind).fadeIn(700).siblings('.tabContent').hide();
});
That's all! You don't need all that ID's all around. Look at the demo!
With a couple of very minor changes you code can be reduced to:
$('.tabContent').hide();
$('.tabs').each(function(){
if($('.tabTrigger.selectedTab',$(this)).length < 1)
$('.tabTrigger:first').addClass('selectedTab');
});
$('.tabTrigger').click(function(){
var content = $(this).data('content');
$(this).parents('div').children('.tabContent').hide();
$(this).parents('div').children('.tabTrigger').removeClass('selectedTab');
$(this).addClass('selectedTab');
$('#' + content).show();
});
$('.tabTrigger.selectedTab').click();
Those changes are
Change the class on the surrounding div to just class="tabs.
Add a data-content attribute with the name of the associated content div
Live example: http://jsfiddle.net/gsTBQ/
Well, I'm a bit behind the times obviously; but, here's my updated version of your demo...
I have updated your fiddle as in the following fiddle: http://jsfiddle.net/4y3Xp/1/.
Basically I just tidied it up a bit, and to refactor I put everything in a separate function instead of having each of the cases in their own. This is basically just putting a new function in that does similar to what yours was doing (e.g. not modifying your HTML model), but I tried to clean it up a bit, and I also just made a function that took the tab number and did each of the items that way rather than needing a separate copy for each.
The main issue with the 'not' part of your query is that the function doesn't return a boolean; like all JQuery queries, it's returning all matching nodes. I just updated that part to return whether .selected was returning more than 0 results; if not, I go ahead and call the code to select the first panel.
Glad you got your problem resolved :)
$(document).ready(function(){
var HandleOne = function (i) {
var idxString = i.toString();
var tabName = '.tabs' + idxString;
var tabContent = tabName + ' .tabContent';
$(tabContent).hide();
var hasSelected = $(tabName + ' .tabTrigger.selectedTab').length > 0;
if (!hasSelected)
$(tabName + ' .tabTrigger:first').addClass('selectedTab');
var selectedTabId =
$(tabName + ' .tabTrigger.selectedTab').attr('id');
var selectedContentId = selectedTabId.replace('tab','content');
$('#' + selectedContentId).show();
$(tabName + ' .tabTrigger').click(function() {
$(tabName + ' .tabTrigger').removeClass('selectedTab');
$(tabName + ' .tabContent').hide();
$(this).addClass('selectedTab');
var newContentId = $(this).attr('id').replace('tab','content');
$('#' + newContentId).show();
});
}
HandleOne(1);
HandleOne(2);
HandleOne(3);
});

Does div with certain content exist

If I have a container div called #container which has a bunch of .inside divs in it, how would I go about checking whether a certain .inside div with a specified content (just a string of English text) exists or not? I'm doing this to prevent duplicates in a notification system I'm setting up for a website. I don't need the actual text - just whether it exists. Also, being able to modify the content of the .inside div if it's found would be good, so I can increment and show the number of times that message has occurred (grouping, if you like).
Thanks,
James
I like using selectors (others have used .filter, which is equally an option).
$(".inside:contains('waldo')").css({color: 'red'});
This is case sensitive.
Use the contains-selector[docs], then the length[docs] property to see how many were found.
var some_string = "test";
var els_with_string = $('#container .inside:contains(' + some_string + ')');
// use .length to check to see if there was at least one
if( els_with_string.length ) {
alert( "at least one already exists" );
}
From the docs:
Description: Select all elements that contain the specified text.
The matching text can appear directly within the selected element, in any of that element's descendants, or a combination thereof. As with attribute value selectors, text inside the parentheses of :contains() can be written as bare words or surrounded by quotation marks. The text must have matching case to be selected.
With respect to modifying the content if found, it would depend on what sort of modification you want to do. I don't know exactly what you mean by grouping.
EDIT: With respect to your comment, you could accomplish what you need like this:
var error = "ERROR:SomeError ";
var el_with_error = $('#container .inside:contains(' + error + ')');
if (el_with_error.length) {
var text = el_with_error.text();
if (/\(\d+\)/.test(text)) {
var new_text = text.replace(/\((\d+)\)/, function(s, g1) {
g1++;
return "(" + g1 + ")";
});
el_with_error.text(new_text);
} else {
el_with_error.text(text + " (2)");
}
} else {
$('#container').append('<div class="inside">' + error + '</div>');
}
Live Example: http://jsfiddle.net/ScZbV/
We could get by without the regular expression if you were able to wrap the grouping quantity in a <span> element.
var error = "ERROR:SomeError ";
var el_with_error = $('#container .inside:contains(' + error + ')');
if (el_with_error.length) {
var span = el_with_error.find('span');
if (span.length) {
var num = +span.text();
span.text( ++num );
} else {
el_with_error.append(" (<span>2</span>)");
}
} else {
$('#container').append('<div class="inside">' + error + '</div>');
}
Example: http://jsfiddle.net/ScZbV/1/
To check existence
$("#container .inside:contains('old text')").size() > 0
To modify the text
$("#container .inside:contains('old text')").text('new text');
Here's a slightly different way of looking at it...
Apply a class name for each "type" of notification. So your notification markup looks like:
<div class="inside error">Error</div>
Then inside of looking for a string inside these divs, use these new class names to your advantage and make use of .find(). If jQuery returns an object then its exists, so do something with it. But if it returns nothing then add it.
Example: http://jsbin.com/imexi4

Can I shorten a string of childNodes[0]?

I have this line of code. It works just fine, but I'm wondering if there's a smarter (read: shorter) way of doing it?
svg.getElementById($(this).attr('id')).childNodes[0].childNodes[0].nodeValue = $(this).val();
I'm using jQuery as well, so any jQuery methods are fine :)
The markup being reached is
<text id=n>
<tspan>text to reach</tspan>
</text>
It would be ideal, however, if I could reach the text even if the tags were removed.
This should let you change the text:
$("#" + $(this).attr("id") + " tspan").text($(this).val());
This might be a kludge, but it'll substitute the end node value of a text element whether it has a tspan element or not. This example acts on an input field with a class of 'replace'.
$('.replace').keyup(function() {
if ($("#" + $(this).attr("id")).has("tspan").length) {
$("#" + $(this).attr("id") + " tspan").text($(this).val());
} else {
$("#" + $(this).attr("id")).text($(this).val());
}
}

Categories

Resources