I'm writing a very conflated Android Unit Conversion application, using all kinds of terrible things to make it pretty. Essentially its executing JS via vebviews, and displaying html formatted according to arbitrary size lists in fancy spinners, with abbreviations underneath... blah blah...
Anyway. Right now I've got somthing like this:
for (l = 0; l < this.slotData.length; l += 1) {
out += '<li>' + this.slotData[l].values[i] + " "+'<li class="sw-right">'+this.slotData[l].abbreviations[i]+'</li></li>';
}
Where slotData[l] represents say "Milimeters" and abbreviations is "Mm", which is underneath, and formatted slightly differently. The idea is that you can enter numbers, and they will show up, underneath the Large Milimeters, next to the small Mm, so that you can see "4400mm".
I'm trying to access and modify the child li, of class "sw-right" at a different point in my code. Wondering if anyone knows an easy way to do this? I can access the parent without trouble, but I'm not sure what the proper way to go from there is...
My access is somthing like this:
this.slotEl[slot].......
Anythoughts?
Thanks guys...
Nathaniel.
You should not try to nest <li> elements. HTML syntax does not permit nesting list items. Since the </li> closing tag is optional, a browser might well just treat your nested item as a sibling of the first one anyway, and discard the second close tag as a double-close on the second item.
Addional: Since you are trying to compose a list item of two parts, maybe what you really want is a definition list. For setting attributes on ranges nested inside of tags, consider using span.
I suggest using jquery:
$('li.sw-right').html('html_you_want_set');
you could use
var myNestedLI = document.getElementsByClassName( 'sw-right' )[ 0 ]
or
var topElements = document.getElementsByTagName( 'li' );
var nestedElements = []
for( var i = 0; i < topElements.length; i ++ )
{
var theseElements = topElements[ i ].getElementsByTagName( 'li' )
for( var j = 0 ; j < theseElements.length; j ++ ) nestedElements.push( theseElements[ j ] )
}
// nestedElements now contains all nested LI elements
As Sparky pointed out, you can't nest <li> tags like that. The second li will be treated as a separate list item from the first, making them siblings. Instead what you want are two separate <div>s inside your <li>. Div is a generic block-level element. (Block-level means there's a line break between them, and generic means that the div's meaning and style is entirely up to you.)
I'd suggest this more descriptive markup:
<li class="calculated-value">
<div class="unit">*large unit name*</div>
<div class="value">*small value with abbreviation*</div>
</li>
In your CSS, the following rule will make the value div display smaller:
li.calculated-value div.value { font-size: 75%; }
(You can add additional rules to give it an indentation, better spacing, bold or color settings, etc. And you can add another line for div.unit that styles that one separately.)
Related
I have a script that gives me the following error: 'TypeError: clickables[ic] is undefined' when I'm checking it with Firebug/in browser consoles. I'm a javascript beginner, who is trying to learn how to do things in vanilla javascript, and so I'm looking specifically for a solution that is just that.
The question: How do I get rid of/silence the undefined TypeError?
What the script should be doing:
I'm using this to reveal hidden elements, whose display attribute is set to none. The script should be getting all the instances of a particular class in a document, .item-reveal, joining that with a unique ID that each item having that class is given, to form a new class to search for via getElementsByClassName. The items with the .item-reveal class are items that are clicked on, the item that is unhidden/revealed has the .ID-reveal-item class (the unique ID of the clickable element followed by the .item-reveal class name reversed, for a simple convention). The ID isn't used for stying at all, it's merely to create a unique class based on a naming convention that can be applied to any pair of elements: one that is clicked on, one that is unhidden/hidden via creating/changing a style for the display attribute.
What the script does:
Currently, the script actually reveals the items onclick, and hides them again on subsequent clicks, and it works with multiple items. So, it kind of, basically, works. I just can't figure out the 'TypeError: clickables[ic] is undefined' issue and how to get rid of it. I get it in several browsers when using developer tools.
The script is an attempt at a self-executing anonymous function sort of thing, so I know the convention is a bit different, but I'm wanting to stick with it so I can apply it to other uses down the road. The article that inspired it is found here:
http://esbueno.noahstokes.com/post/77292606977/self-executing-anonymous-functions-or-how-to-write
EXAMPLE:
HTML
<!-- Item to be clicked, with unique ID -->
<h3 class="item-reveal" id="plan-1">Click for special pricing!</h3>
<p>An introductory paragraph...</p>
<!-- Hidden item to be revealed, will always have a unique class -->
<p class="plan-1-reveal-item">Special, this month only: $49.99</p>
<h3 class="item-reveal" id="plan-b">Click for special pricing!</h3>
<p>An introductory paragraph...</p>
<p class="plan-b-reveal-item">Special, this month only: $29.99</p>
CSS
/* Init - hide/unhide onclicks */
.item-reveal {cursor:pointer;}
[class$="-reveal-item"] {display:none;}
/* Halt - hide/unhide onclicks */
javascript:
var clickables = document.querySelectorAll('.item-reveal');
var clickCount = clickables.length;
(function () {
var Reveal = {
swapThis: function () {
for (var ic = 0; ic <= clickCount; ic += 1) {
// Next line seems to create the error message.
clickables[ic].onclick = function (unhideHide) {
var hidden = this.id;
var findClass = hidden += '-reveal-item';
var revealSwap = document.getElementsByClassName(findClass);
for (rn = 0; rn < revealSwap.length; rn++) {
revealSwap[rn].style.display = (revealSwap[rn].style.display == 'block') ? 'none' : 'block';
}
}
}
}
}
Reveal.swapThis();
}) ();
The script is linked via a SCRIPT tag, just prior to the closing BODY tag. I have tried it with both Async and Defer attributes, with and without other scripts in an HTML document, and the result is the same. I tried adding an event handler to ensure it wasn't something with the DOM loading still ongoing, but I'm not sure how to really test for that to see if it was actually doing anything. Unit testing is something that I'm just starting to attempt familiarizing myself with.
I'm trying to knock the dust off skills after several years in a completely unrelated industry, so the last year has been all about catching up on web development technologies, learning responsive design and HTML5 data stuff, and trying to learn javascript. I've searched, read, and bought several ebooks/books, and this is one of the few times I've run into something I just can't figure out. I imagine it's probably something simple and obvious to someone with formal programming/scripting knowledge, but I was an eBusiness major and networking, marketing, server/systems support, cabling, HTML/CSS, etc., are where I'm comfortable. Any help is greatly appreciated, but keep in mind that I'm trying to implement this in an environment/project that will have no jQuery, by choice. Thanks!
You are going off the end of the list with this:
for (var ic = 0; ic <= clickCount; ic += 1)
Change it to this:
for (var ic = 0; ic < clickCount; ic += 1)
clickCount is the length of the list so since it's 0 based indexing, clickables[clickCount - 1] is the last element in the list. You were trying to access clickables[clickCount] which does not exist.
I got a HTML page with two div/class elements with the same name, called "notifications". However, I want to count only the latter ones and dont count the first one.
The first one looks like this:
<a href="mynotifcations"><div class="notification">1</div>
This one should be excluded.
The later ones look like this:
<div class="notiheader"><span class="notification">2 notifications</span>
Right now I get the notification like this
document.getElementsByClassName("notification");
If I cycle through it, it returns "1" and then "2 notifications".
I would rather get merely the "2 notifications", or better yet just the number 2 as an integer.
How do I manage to do achieve that? I'm really running out of ideas :/
I would have to say that is a strange setup, but here is a way:
<script>
var special = document.querySelectorAll( "span.notification" );
alert (special[1].innerHTML);
</script>
It might be better to add a class to distinguish them (that's what they're for), but if you must, you can use document.querySelectorAll() to match the specific ones you're looking for:
document.querySelectorAll("div .notification")
This will only match divs with the notification class.
var elementsWanted = document.querySelectorAll("div .notification");
for(var i = 0; i < elementsWanted.length; i++){
elementsWanted[i].style.backgroundColor = "yellow";
}
<div class="notification">Span</div>
<div class="notiheader"><span class="notification">Div</span>
<div class="notiheader"><span class="notification">Div</span>
<div class="notiheader"><span class="notification">Div</span>
<div class="notiheader"><span class="notification">Div</span>
Try this, here we first get all the notification classes, x.length gives the total no of notification classes in the html. Then do your stuff based on its index(zero based index).
function hookSecondNotification() {
var x = document.getElementsByClassName("notification")[1];
// x.style.backgroundColor = "red";
// here do your stuffs.
}
I am relatively new at javascript, and found an interesting behavior that I can't explain today. I have a custom <hr> (with an image) on a website, which displays oddly in IE7 and below. To overcome this, I wanted to use replaceChild() in combination with getElementsByTag(). Initially, I simply tried to loop over the list, so:
var hrules = document.getElementsByTagName('hr');
for (var i=0; i < hrules.length; i++) {
var newHrule = document.createElement("div");
newHrule.className = 'myHr';
hrules[i].parentNode.replaceChild(newHrule, hrules[i]);
document.write(i);
}
However, this does not work: it actually only gets half the elements, skipping every other one. Printing i gives half-integer values of the actual number of <hr> elements in the document (e.g. if there are 7 <hr/> elements, it prints 4. By contrast, the following does work:
var hrules = document.getElementsByTagName('hr');
var i = 0;
while (i < hrules.length) {
var newHrule = document.createElement("div");
newHrule.className = 'myHr';
hrules[i].parentNode.replaceChild(newHrule, hrules[i]);
document.write(i);
}
i is printed the same number of times as there are hrules in the document (but of course is always 0, since I'm not incrementing it), and the hrules are replaced correctly. I recognize that the while here might as well be while(true)--it's just going until it runs out of <hr> elements, but appears to stop after that (it's not printing any more 0s).
I've tried this with a number of different types of elements, and observed that this only occurs when replacing one kind of element with another. I.e., replacing p with div, span with p, etc. If I replace p with p, div with div, etc. the original example works correctly.
Nothing in the documentation I've found (w3schools, various Google search, here, etc.) suggests an obvious answer.
What is going on here? First, why does the second example I offered work - is replaceChild() iterating over the elements automatically? Second, why is the behavior different for different types of element?
document.getElementsByTagName is a live access to all the HR elements in the document - it's updated whenever you change the document. You don't get a snapshot of all the HRs in the document whenever you call it.
So, with the first code, you are both incrementing i and reducing the size of hrules.length each time round the loop. This explains why you only see half the steps you expect.
Here's the solution I ended up using, in case anyone else (like #Pav above) is curious.
var hrules = document.getElementsByTagName('hr');
/* Each repetition will delete an element from the list */
while (hrules.length) {
var newHrule = document.createElement("div");
newHrule.className = 'ieHr';
/* Each iteration, change the first element in the list to a div
* (which will remove it from the list and thereby advance the "head"
* position forward. */
hrules[0].parentNode.replaceChild(newHrule, hrules[0]);
}
Essentially, what happens is you get a list of all the hrules in the document. This list is dynamically updated as you interact with it (see Matthew Wilson's answer). Each time you change the first element of the list to a div, it gets removed from the list, and the list is updated accordingly. The result is that you simply need to act on the first element of the list each time until the length of the list is 0.
That's admittedly a little counterintuitive, but it's how the list works.
I'm struggling to decipher a way to remove several specific href elements which contain no IDs and are children of individual parents with no IDs.
The best I can manage is identifying the four offending, out of 8 or 9 href tags (and the number may vary), by a specific word within the URL itself. For this, I do the following:
<script language=javascript>
var xx = document.getElementById('theID').getElementsByTagName('a');
var ptn=/\=media/;
for(var i=0; i<xx.length; i++) {
if(ptn.exec(xx[i])){
alert(xx[i]);
}
}
</script>
Of course all this gives me is the four specific URLs within the href where "=media" is present. Now, somehow, I need to be able to remove either these href elements, or their parent elements (which happen to be unordered list tags). It's not until I get a level higher (table cell) that I gain access to an element ID, or anything distinguishing besides a particular word within the URL itself.
I'm open to any approach at this point - PHP may be an option (I really haven't explored this yet), but for this, javascript was my first logical choice. I can't tamper with the page that generates the links directly, only a secondary page which gets included at page load time.
Any pointers on how to solve this??
======================== final solution =====================
<script language=javascript>
var xx = document.getElementById('theID').getElementsByTagName('a');
var ptn=/\=media/;
for(var i=0; i<xx.length; i++) {
while(ptn.exec(xx[i].href)){
alert(xx[i]);
xx[i].parentNode.removeChild(xx[i]);
}
}
</script>
You don't need the ID to remove an element. You only need a reference to the element (which you seem to have).
instead of this:
alert(xx[i]);
try this:
XX[i].parentElement.removeChild(xx[i]);
You can call removeChild() on the parent element, like so:
xx[i].parentNode.removeChild(xx[i]);
As a side note, your regular expression isn't being executed on the href property. Change your if statement to:
if(ptn.exec(xx[i].href)){
var parent = xx[i].parentNode;
parent.removeChild(xx[i]);
http://www.onlinetools.org/articles/unobtrusivejavascript/chapter2.html has some nice examples of similar operations (scroll down).
This is a challenge question / problem. Hope you find it interesing.
Scenario: You have a very long list (unreasonably long) in a single column. It would be much better displayed in multiple shorter columns. Using jQuery or another tool, what do you do?
The format of the list is as follows:
<div class="toc">
<dl>
<dt>item 1</dt>
<dd>related to 1</dd>
<dt>item 2</dt>
<dd>related to 2</dd>
<dt>item 3</dt>
<dd>related to 3</dd>
<dt>item 4</dt>
<dd>related to 4</dd>
<dt>item 5</dt>
<dd>related to 5</dd>
<dt>item 6</dt>
<dd>related to 6</dd>
<dt>item 7</dt>
<dd>related to 7</dd>
<dt>item 8</dt>
<dd>related to 8</dd>
<dt>item 9</dt>
<dd>related to 9</dd>
<dt>item 10</dt>
<dd>related to 10</dd>
</dl>
</div>
Caveat: The dd's may contain nested dl's, dt's, & dd's.
Also be sure to keep related items in the same column (ie. if dt 7 is col x, so should dd 7).
This problem inspired by the somewhat ridiculously laid out Zend Framework manual.
Edit: See below for answer.
i would do a count of the array of $("dt") then if it's over a certain size inject a closing and opening then using styling to float them into columns.
Josh
Ignoring:
Also be sure to keep related items in the same column (ie. if dt 7 is col x, so should dd 7).
one possible solution could be column-count, e.g:
http://pici.se/pictures/small/vekPcSkFE.png
However, it's a part of CSS 3 so browser support ... you know. :(
I would first write the complete data in a json then check the amount of dt's + dd's + related children amount by considering this sum as total row amount.
Then i would check the predefined row amount per column and divide this json into proper parts.
And finally from this parts i'd create columns without breaking related items.
Sinan.
Answer So this problem is more difficult than it first appears.
My initial thought was that I would wrap the column in <table><tr><td></td></tr></table> and then after every nth parent dd output a </td><td>. Simple, right? Except you can't really output a closing tag like that. Ultimately when you write $('</td><td>').after('.toc > dl > dd'), you'll be creating nodes - that's nodes with opening and closing tags. Since you must create nodes, the browser will ignore the first closing tag.
Well, let's say that you solve that problem somehow. What are you're iteration conditions? My first attempt was to construct a for loop. It seems reasonable. For every nth parent dd, do whatever you need to do. However, how do you construct those conditions in jQuery? You have less than, greater than, and equal. But you don't have greater than or equal to (>=) or less than or equal to (<=), and this is a critical distinction.
You might try something like this:
for (i = 0; i < len; i+=40) { // where 40 determines the # per col
get elements i thru i+40;
wrap this set and float left
}
So how would you do this in jQuery?
// note that you must use > to prevent nested descendants from being selected
var len = jQuery('.toc > dl > dd').size()
for (i = 0; i < len; i+=40) {
// this selector says give me the first 40 child elements
// whatever type of element they may be
$('.toc > dl > *:gt('+i+'):lt('+(i+40)').wrapAll('<div style="float:left">');
// however because we don't have >= and :gt won't accept negatives as an input
// we must either do a special case for the first iteration
// or construct a different selector
$('.toc > dl > *:eq('+i+')', ' +
'.toc > dl > *:gt('+i+'):lt('+(i+40)')
.wrapAll('<div style="float:left">');
}
You could also do something with jQuery's add() method to add the first element of each iteration to your set, but you must maintain document order in your selection or jQuery will rearrange the set, so you have to do that first.
Ultimately, the for loop made sense initially, but it ran into problems with challenging selectors. Of course, we're not using the $('selector').each(function () { }); construct because that would only be useful if we could output independent closing tags.
So what did I end up with? Final Answer:
$('.toc').after('<div id="new"></div>');
do {
var curSet = $('.toc > dl > *:lt(40)')
.appendTo('#new').wrapAll('<div style="float:left"></div>');
} while (curSet.size());
This approach appends a new div after the old one. Then iteratively grabs the first 40 elements from the old and appends them to the new div after wrapping them in a div that will float left, looping as long as there are elements left to grab, and it maintains order.
Not terribly complicated after you figure it out, but there were a few gotcha's throughout the problem that I hope you find interesting. I did.
To finish up the ultimate goal of making the documentation significantly more useful:
I added some style and used the dt's as togglers to show the dd's. I also used a simple php proxy wrapper (5-10 LOC) so I could bring in any given, desired doc page thru an ajax call without remote ajax warnings.
I ended up with a nice little document in a single, navigable page that loads in < 2 secs (and uses ajax to load all subsequent pages in < 1 sec) rather than a monstrous page that takes 15-20 sec to load per page!
Problem solved. Something much more enjoyable and useful in 10-15 lines of javascript (total with the reorganizing, toggling, and ajax code), < 10 lines of PHP, and a few style rules.
No more slow Zend docs and endless scrolling.