Constructing HTML elements with readability and maintenance in mind - javascript

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();

Related

How to change Jquery selector based on an param passed through a function

I have a function:
function factCheck(index) {
if (arrayOfSites[index].indexOf("pdf") > -1) {
$('#20').attr('style', 'color: red');
$('#' + index).attr('style', 'color: red');
console.log('index: ' + index);
console.log($("#" + index).text());
}
}
So my question is. The text color of the element changes color when I use $('#20') but when I use, $('#' + index) it doesn't work.
Funny thing is, I with console.log.. it logs the text of the element but I can't effect the css of it.
Why is this happening?
// after a three hour meeting.. I came back with some really great answers!! Thank you!!
edit:
the code below shows how I'm snagging all the links on the page and add the id equal to the index of that item. So that's why I'm trying to grab that link, and effect it in some way. I appreciate all you guys.. I think I'm going to take the string and add a letter to it as they come in through the function and then manipulate the anchor from that point. I just wonder if there's a more efficient way of doing this.
$(".lpage a").each(function (index) {
// console.log(index + ": " + $(this).text());
str = $(this).attr('href');
arrayOfSites.push(str);
str = arrayOfSites[index];
title = $(this).attr('title');
parseURL(str);
$('.colContent2').append(cellOpen + '<a onclick="whichFunction(' + index + ');" id= "' + index + '"style="cursor:pointer;" class="injectedLinkCol2" >' + str + '</a>' + cellClose).prop("id", index);
});
Maybe it has something to do with the name of your id attribute. Take a look at this answer.
Try to use the toString() function:
function factCheck(index) {
if (arrayOfSites[index].indexOf("pdf") > -1) {
$('#20').attr('style', 'color: red');
$('#' + index.toString()).attr('style', 'color: red');
console.log('index: ' + index);
console.log($( "#" + index.toString() ).text());
}
}
An id name or class name must begin with a name must begin with an underscore (_), a hyphen (-), or a letter(a–z).
So something like
'#d20'
would work.
See this: Valid CSS Selectors.
I couldn't reproduce the exact problem. I made a pen (link) and tried what you asked but it works well. So it must be some error in the remaining code.
on a related note
In CSS id's are not allowed to start with a number(classes are allowed). So writing something like
#20{
color: red;
}
won't work, but the rule only applies to css. JQuery will still work, which means your only option's are to write inline styles or use JQuery's .attr or .css, but jQuery.attr() will reset all your inline styles. you are left with using .css(). So, it's better to not start your id's with numbers.
try using .css instead of .attr and see if it works.
$('.exampleClass:eq(' + index + ')').css("color", "yellow");
for some reason works
$('.exampleClas').eq(index).css("color", "yellow");
does not work.

keep track of the position of a div in a textarea

I have a textarea that keeps track of a moveble div like this
$('#content').children().draggable({
drag : function () {
$('#textarea').text("left:" +($(this).position().left) + "px" + "\ntop:" + $(this).position().top + "px");
}
});
The problem is that if i write something in that textarea, it will stop updating the position if i move the div.
If the text in the textarea is like this:
blablablabla
left:10px
top:20px;
blablablablabla
I want to be able to write in the textarea and update the position if i move the div without the other content of the textarea being removed.
someone should be able write whatever they want in the textarea and if they move it, the position will appear a specific place or at the end of what they have written
Any ideas?
Example using ".val" instead of ".text": http://jsfiddle.net/Ydkrw/
".val" will remove the existing text...
Update: based on valentinos answer i did like so: http://jsfiddle.net/8G82U/
But this doesn't work if you write something in the textarea before you move it
This could be something to get you started. This is just a rough design that might help you. Used substring and replace javascript methods.
Fiddle
You could save the string denoting the position in variable, and replace only that string when you drag the div. Like this:
var position;
var oldposition = '';
$('#content').children().draggable({
drag: function () {
position = "left:" + ($(this).position().left) + "px" + "\ntop:" + $(this).position().top + "px";
$('#textarea').val($('#textarea').val().replace(oldposition, position));
oldposition = position;
}
});
http://jsfiddle.net/kCT9f/
Typically when I'm doing something like this I add a replacement keys to the content and replace it when I need to prepare the content.
<textarea>blablablabla {left: 10px} {top: 20px} blablablablabla</textarea>
Update the keywords when the div is moved like this.
$('#content').children().draggable({
drag: function(){
var text = $('#textarea').text();
// Replace Keys
text = replaceLengthKey(text, "left", $(this).position().left);
text = replaceLengthKey(text, "top", $(this).position().top);
// Return changes
$("#textarea").text(text);
}
});
function replaceLengthKey(source, name, value){
// Absolute length units: https://developer.mozilla.org/en-US/docs/CSS/length#Absolute_length_units
return source.replace(RegExp("\\{" + name + ":\\s*[0-9]*(px|mm|cm|in|pt|pc)?\\}", "gm"), "{" + name + ": " + value + "}")
}
Then, when you prepare the content as you want on the server or client, but at least the structure will be consistent.
You can enclose the position text within brackets and then when the div is moved apply a regular expression and change only the text within the brackets. Here is a working example.
var text=$('#textarea').val();
var newPos = "(left:" + ($(this).position().left) + "px" + "\ntop:" +($(this).position().top) + "px)"
text=text.replace(/ *\([^)]*\) */g, newPos);
$('#textarea').val(text);

Javascript - Jquery on the fly id retrieving

I've made a JS utility with jQuery that gives IDs to div elements and to links, which are coordinated.
Example:
div id="container"
div id="1"
div id="2"
div id="links"
a name="1"
a name="2"
So once the page is loaded, when I click on the first link (name="1") it is supposed to show the first div (id="1").
But it seems that as my IDs are created on the fly, the function doesn't seem to find the right div.
Here's my function:
$("#video_grid .grid_item a").each(
function(i) {
/* FINDING 'A' AND GIVING THEM NAME */
$(this).addClass("item" + (i + 1));
$(this).attr("name", "#" + (i + 1));
/* BACKGROUND */
$(this).css("background-image",
"url(images/visu_" + (i + 1) + ".jpg");
$(this).hover(
function() {
$(this).css("background-image",
"url(images/visu_hover_" + (i + 1) + ".jpg")
},
function() {
$(this).css("background-image",
"url(images/visu_" + (i + 1) + ".jpg");
});
});
/* FINDING DIV GIVING THEM IDS */
$("#capsule_player > div").each(function(i) {
$(this).addClass("item#" + (i + 1));
});
/* HERE'S THE PROBLEM */
$("#capsule_player div:first-child").css("display", "block");
/* appel la div correspondante avec la video */
$(".grid_item a").live("click", function() {
var divname = $(this).attr("name");
alert(divname);
/* WORK FINE TIL HERE */
/* THIS IS THE REAL PROBLEM */
$(".item" + divname).show("slow").siblings().hide("slow");
/* ALERT IF YOU FIND THE DIV WITH THE ID, DOES NOT WORK */
if ($(".item" + divname)[0]) {
alert('tonton');
}
});
I'm using jQuery, basic HTML and CSS, and PHP is not allowed.
Your function that is supposed to give the div's their IDs is giving them a class. You should change it to something like this:
$("#capsule_player > div").each(function(i){
$(this).attr("id", 'item' + (i+1));
});
That should work, but the rest of the code could do with a tidyup as there a better/more semantic ways to do what you want to do (which I might do when I'm not busy).
EDIT:
Something like this would be better (based on Armatus' comment):
// give each div a data-index attribute
$("div.item").each(function(i){
$(this).attr("data-index", i);
});
// give each anchor a data-index attribute
$("#video_grid .grid_item a").each(function(i) {
$(this).attr("data-index", i);
});
$(".grid_item a").live("click",function () {
var index = $(this).attr("data-index");
$('div.item[data-index="' + index + '"]').show();
});
.live() is deprecated, but I'll leave it because I don't know which version of jQuery you're using.
Although the route that Christian shows you is probably in the long run better and surely more learningful, your original problem can easily be solved by changing two lines:
$(this).attr('name', '#' + (i + 1));
[...]
$(this).addClass('item#' + (i + 1));
into
$(this).attr('name', 'Q' + (i + 1));
[...]
$(this).addClass('itemQ' + (i + 1));
The immediate problem is that you are giving your div a classname which includes a '#' sign. This will literally become part of your class name. However, during selection, this '#' sign will be interpreted as a 'ID' selector. So that doesn't work.

Trouble with jquery's append and prepend

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.

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