# JQuery oder JavaScript: Inhaltsverzeichnis erstellen



## Linus_for_you (16. August 2011)

Für eine HTML-Softwaredokumentation, die nur offline verwendet wird,
brauche ich eure Hilfe.

Und zwar will ich ein Inhaltsverzeichnis erstellen, dass alle Überschriften dynamisch erfasst und daraus ein sinnvoll durchnummeriertes Inhaltsverzeichnis erstellt.

Ich hab aber keine Ahnung wo ich da genau anfangen soll, da ich noch ein Anfänger im Hinblick auf JavaScript bin. 

die Struktur der Überschriften ist immer gleich. Es gibt ein DIV mit einer Klasse die immer mit "gsc" anfängt und danach eine <h1>
also z.B. so

```
<div class="gscTitel">
    <h1>Ich bin die Überschrift</h1>
         <div class="gscSubTitel">
              <h1>Ich bin die 2. Überschrift</h1>
         </div>
</div>
```

diese Struktur kann leider nicht verändert werden (also z.b. mit h2, h3, h4 etc erweitert werden)

das erste Resultat sollte sein:
1 Ich bin die Überschrift
1.1 Ich bin die 2. Überschrift

nach Möglichkeit sollen die Überschriften 2. Ordnung eingerückt sein.
Im absoluten Idealfall soll vor den Überschriften 2. Ordnung und niedriger ein "+" stehen (Icon), über dass man die Überschrift und alle Überschriften darunter zusammenklappen (ausblenden) kann.

Ich hoffe ihr könnt mir helfen. Ich freue mich über alle hilfreichen Tipps und Codebeispiele.


----------



## CPoly (16. August 2011)

Man kann so was innerhalb von Minuten runter hacken (dank jQuery). Hast du denn schon was versucht?

Erstmal würde ich alle Überschriften der ersten Ebene Selektieren. Dann über diese iterieren und mir alle Unterpunkte holen. Und das für alle Ebenen. Da springt einem quasi Rekursion sofort ins Auge.

Wird das irgendwie kommerziell verwendet oder ist das für dich privat?


----------



## Linus_for_you (16. August 2011)

Es ist für die Softwaredokumentation hier an der Hochschule.
Ich weiß jetzt nicht ob das für dich als kommerziell zählt,
aber privat ist es nicht. 

Ja ich hab schon vermutet, das jQuery das Mittel der Wahl ist. JQuery is echt toll! 



> Erstmal würde ich alle Überschriften der ersten Ebene Selektieren.


Tja und da fängt es schon an. Ich hab in der JQuery Doku schon sehr intesiv gesucht aber nichts passendes gefunden.

Wenn es dir nicht zu viele Umstände macht, könntest du mir dann vielleicht etwas konkreter beschreiben wie du vorgehen würdest? Also vielleicht kleine Codebeispiele geben damit ich lernen kann wie das genau funktioniert?


----------



## CPoly (16. August 2011)

Die jQuery Doku ist zwar hervorragend, aber zum Selektieren benutzt man CSS-Selektoren. Die findest du dort nicht .

Kannst du bitte etwas mehr HTML zeigen? Aus den zwei Ebenen an Überschriften lässt sich nicht erkennen, wie z.B. zwei Überschriften auf der gleichen Ebene aussehen oder die dritte Ebene.

EDIT:

```
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
	
	<title>TOC</title>
</head>

<body>
	<div>
		<h1>Ich bin die Überschrift</h1>
		
		<div>
			<h1>Ich bin die 2. Überschrift</h1>
			
			<div>
				<h1>Ich bin die 3. Überschrift</h1>
			</div>
			
			<div>
				<h1>foobar Ich bin die 3. Überschrift</h1>
				
				<div>
					<h1>Bacon</h1>
				</div>
			</div>
		</div>
	</div>
	<div>
		<h1>Foo Ich bin die Überschrift</h1>
		
		<div>
			<h1>Foo Ich bin die 2. Überschrift</h1>
			
			<div>
				<h1>Foo Ich bin die 3. Überschrift</h1>
			</div>
		</div>
	</div>
	<div>
		<h1>Bar Ich bin die Überschrift</h1>
		
		<div>
			<h1>Bar Ich bin die 2. Überschrift</h1>
			
			<div>
				<h1>Bar Ich bin die 3. Überschrift</h1>
			</div>
		</div>
	</div>
	
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"></script>
	<script type="text/javascript">
	/* <![CDATA[ */
	function walk( toc , $elem ) {
		if( ! $elem.size() ) {
			return;
		}
		
		var $kids = $elem.find( '> div' );
		
		$kids.each(function() {
			var $heading = $(this).find( '> h1' );
			
			if( $heading.size() ) {
				return walk( toc[ $heading.text() ] = {}, $(this) );
			}
		});
	}
	
	$(function() {
		var toc = {};
		
		walk( toc , $('body') );
		
		alert( JSON.stringify( toc ) );
	});
	/* ]]> */
	</script>
</body>

</html>
```

Liefert folgendes JSON als Inhaltsverzeichnis. Daraus jetzt Listen zu generieren ist nochmal eine völlig andere Baustelle.


```
{
    "Ich bin die Überschrift": {
        "Ich bin die 2. Überschrift": {
            "Ich bin die 3. Überschrift": {},
            "foobar Ich bin die 3. Überschrift": {
                "Bacon": {}
            }
        }
    },
    "Foo Ich bin die Überschrift": {
        "Foo Ich bin die 2. Überschrift": {
            "Foo Ich bin die 3. Überschrift": {}
        }
    },
    "Bar Ich bin die Überschrift": {
        "Bar Ich bin die 2. Überschrift": {
            "Bar Ich bin die 3. Überschrift": {}
        }
    }
}
```


----------



## Linus_for_you (17. August 2011)

Hier mal ein kleiner Abschnitt aus dem Quelltext.
Du siehst hier quasi Kapitel 5 und 6. 
Es gibt schon ein kleines JavaScript-File, dass über die Datei läuft und die Kapitel richtig nummeriert. (Kommt nicht von mir, sondern hab ich irgendwo im Netz gefunden)
"Übersicht" kriegt dann die 5, "Programm" die 5.1, "Foo bar" die 5.1.1 etc.
Und diese Nummerierung muss ich ja auch irgendwie in das Inhaltsverzeichnis kriegen.

```
<div class="gscOverview">
<h1>&Uuml;bersicht</h1>
    <div class="gscProgramm">
    	<h1>Programm</h1>
        <div class="gscFoobar" widget="foobar">
            <h1>Foo bar</h1>

            <div class="gscContentBlock">
				<div class="gscFooVisual"><img src="images/content/foobar.png"></div>
				<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.  </p>  
                         
            </div>
        </div>
    </div>
    <div class="gscgsceddableFoo">
    	<h1>Foo widgets</h1>

        <div class="gscBar" widget="Bar">
            <h1>Bar Foo</h1>
            <div class="gscContentBlock">
            	<div class="gscWidgetBar"><img src="images/content/barfoo.png"></div>
				<p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. </p>
                      
            </div>
        </div>
    </div>  

</div>    

<div class="gscWidgets">
	<h1>Widgets Description</h1>
    <div class="gscContentBlock">
        <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
        
    </div>        

	<div class="gscWidgetSpecification" widget="Barfoobar">
    	<h1>Widget Bar Foo Bar</h1>
    	<div class="gscWidgetDescription">

        	<p>Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
            
        </div>
        <div class="gscLorem">
            <h1>Lorem</h1>
            <div class="gscConsetetur">
            	<h1>Consetetur Sadipscing</h1>
                 <div class="gscBlock">

                    <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>

					<img src="images/content/widgetbarfoo.png">          

                    <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
      			 
                 </div>
            </div>
            <div class="gscDolores">
                <h1>Dolores</h1>

                <div class="gscContentBlock">
                    <img src="images/content/dolores.png">
                    <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
                    <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>    
                                                        
                </div>
            </div>   
            
        </div>
```

hab mir dein kleines Script eben angeschaut bin aber nicht sehr schlau daraus geworden...


----------



## CPoly (17. August 2011)

Wie sieht denn das Skript aus dem Internet aus? Vielleicht lässt die die Inhaltsverzeichnis-Funktionalität einfach dort integrieren. Ich kann mich im Moment nicht konzentrieren, hatte heute morgen Klausur.


----------



## Linus_for_you (17. August 2011)

So, hier das Script

```
var counter = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
function updateHeadlines(root, level) {
	var childs = root.childNodes;
	for (var i=0; i<childs.length; i++) {
		if(childs[i].nodeName=="H1") {
		  if(level>0) counter[level-1]++;
		  if(level<7) childs[i].innerHTML = getNumbering(level) + " " + childs[i].innerHTML;
		  childs[i].style.fontSize = "" + (28 - level*2) + "px";
		}
		if(childs[i].nodeName=="DIV" && childs[i].className!="gscTitel") {	  
		  updateHeadlines(childs[i], (level+1));
		}
	}
	counter[level] = 0;
	
}

function getNumbering(level) {
	var str = "";
	for(var i=0;i<level;i++) str += (str!=""? "." + counter[i] : counter[i]);
	return str;
}
```

Wie war denn die Klausur? Gut gelaufen?


----------



## CPoly (17. August 2011)

Wie war denn die Klausur? Gut gelaufen?

Ja, war ok. Ich bin nur irgendwie im Moment nicht in Höchsform. Es ging in der Klausur um AJAX, PHP, XSLT und XSD (Am Computer).

Ich hab meinen Code von weiter oben etwas erweitert. Ich muss jetzt leider wieder weg. Die restlichen Punkte bekommen wir auch noch hin.


```
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
	
	<title>TOC</title>
</head>

<body>
	<div class="gscOverview">
	<h1>&Uuml;bersicht</h1>
		<div class="gscProgramm">
			<h1>Programm</h1>
		    <div class="gscFoobar" widget="foobar">
		        <h1>Foo bar</h1>

		        <div class="gscContentBlock">
					<div class="gscFooVisual"><img src="images/content/foobar.png"></div>
					<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.  </p>  
		                     
		        </div>
		    </div>
		</div>
		<div class="gscgsceddableFoo">
			<h1>Foo widgets</h1>

		    <div class="gscBar" widget="Bar">
		        <h1>Bar Foo</h1>
		        <div class="gscContentBlock">
		        	<div class="gscWidgetBar"><img src="images/content/barfoo.png"></div>
					<p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. </p>
		                  
		        </div>
		    </div>
		</div>  

	</div>    

	<div class="gscWidgets">
		<h1>Widgets Description</h1>
		<div class="gscContentBlock">
		    <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
		    
		</div>        

		<div class="gscWidgetSpecification" widget="Barfoobar">
			<h1>Widget Bar Foo Bar</h1>
			<div class="gscWidgetDescription">

		    	<p>Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
		        
		    </div>
		    <div class="gscLorem">
		        <h1>Lorem</h1>
		        <div class="gscConsetetur">
		        	<h1>Consetetur Sadipscing</h1>
		             <div class="gscBlock">

		                <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>

						<img src="images/content/widgetbarfoo.png">          

		                <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
		  			 
		             </div>
		        </div>
		        <div class="gscDolores">
		            <h1>Dolores</h1>

		            <div class="gscContentBlock">
		                <img src="images/content/dolores.png">
		                <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.</p>
		                <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>    
		                                                    
		            </div>
		        </div>   
		        
		    </div>
	
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"></script>
	<script type="text/javascript">
	/* <![CDATA[ */
	function walk( toc , $elem ) {
		if( ! $elem.size() ) {
			return;
		}
		
		var $kids = $elem.find( '> div' );
		
		$kids.each(function() {
			var $heading = $(this).find( '> h1' );
			
			if( $heading.size() ) {
				return walk( toc[ $heading.text() ] = {}, $(this) );
			}
		});
	}
	
	function output( toc , numbering ) {
		var list = '';
		var counter = 1;
		
		for(var k in toc) {
			if(toc.hasOwnProperty(k)) {
				list += '<li>' + getNumbering(numbering, counter) + ' ' + k + '</li>'
				
				list += output( toc[k] , getNumbering(numbering, counter) );
				
				counter++;
			}
		}
		
		if(list.length) {
			return '<ol style="list-style-type:none;">' + list + '</ol>';
		} else {
			return '';
		}
	}
	
	function getNumbering( numbering , counter ) {
		if(numbering === '') {
			return counter;
		}
		
		return numbering + '.' + counter
	}
	
	$(function() {
		var toc = {};
		
		walk( toc , $('body') );
		
		var lists = output( toc , '' );
		
		$('body').prepend(lists);
	});
	/* ]]> */
	</script>
</body>

</html>
```


----------



## Linus_for_you (18. August 2011)

ok super.. also das läuft schon mal soweit.
das nächste Problem ist nun, wie ich die Anker-Links im Dokument selbst setze (manuell oder evt sogar automatisch)? Und wie ich dann in der Inhaltsliste die passenden Überschriften mit den entsprechenden Anker verlinke...


----------



## CPoly (18. August 2011)

Müssen es Anker sein?


```
//Hier könntest du noch sinnvollere Selektoren nehmen, weil es kommen bestimmt neben dem Inhaltsverzeichnis noch andere Listen vor

var items = $('li');
var heading = $('h1');

items.click(function() {
	var i = items.index( this );
	var h = heading.eq( i );
	
	$(window).scrollTop( h.offset().top );
});
```


----------



## Linus_for_you (18. August 2011)

ok, das muss ich um es zu verstehen mal eben auseinandernehmen...
also ich definiere als "items" das element 'li'. Damit erfasse ich also alle li-elemente auf der seite, richtig?
und mit heading das gleiche mit den H1

wenn ich jetzt also auf ein li klicke, dann wird eine funktion aufgerufen. diese funktion speichert zu erst das element auf das ich geklickt hab in eine variable i und ordnet dem dann, die entsprechende h1 zu. und dann soll das window zu der headline "h" scrollen. richtig?

also ich habs jetzt in die datei ganz unten eingebaut, aber ein klick bewirkt leider nicht, dass ich scrolle. kann das daran liegen, dass ich einen besseren selector nehmen sollte für "items"?


----------



## CPoly (18. August 2011)

Linus_for_you hat gesagt.:


> wenn ich jetzt also auf ein li klicke, dann wird eine funktion aufgerufen. diese funktion speichert zu erst das element auf das ich geklickt hab in eine variable i und ordnet dem dann, die entsprechende h1 zu. und dann soll das window zu der headline "h" scrollen. richtig?



Nicht ganz. Wenn du auf ein li klickst, hole ich mir mittels "index" den Index des geklickten Elements unter allen anderen. Und dann hohle ich mir die Überschrift, die unter allen Überschriften eben diesen Index hat. Ich nutze einfach die Tatsache aus, dass es zu jeder Überschrift genau einen Inhaltsverzeichniseintrag gibt und das in der gleichen Reihenfolge (Die Elemente liegen in den jQuery Objekten in der Reihenfolge wie im Dokument).


Wie genau hast du es eingebaut? Das muss mit in die ready-Funktion.


```
//Das ist identisch zu $(document).ready(function() {
$(function() {
    var toc = {};
		
    walk( toc , $('body') );
	
    var lists = output( toc , '' );
		
    $('body').prepend(lists);

    //HIER HIN
});
```


----------



## Linus_for_you (18. August 2011)

```
//Das ist identisch zu $(document).ready(function() {
$(function() {
```
 
achsoooooooooo, ok alles klar.. jetzt gehts auch!
Super Geil!! Hammer!!

ok, jetzt noch das zusammen klappen..
also im Initialzustand, sollen alle Überschriften zusammen geklappt sein und man soll nur die Überschriften erster Ordnung sehen.
ich hab mir eine Grafik geladen (ein kleines "+") und es soll jetzt so sein, dass wenn ich auf das "+"-Klicke, dass dann die entsprechend nächste Überschriftenordnung ausklappt. 

bsp: 

```
+1. Titel
+2. Programm
```

klick auf das + bei 2:


```
+1. Titel
+2. Programm
   +2.1 Foo
   +2.2 Bar
```
und so weiter

hat eine Überschrift keine "Kinder" soll auch das "+" nicht angezeigt werden.

wie stelle ich dass, dann an?


----------



## CPoly (18. August 2011)

Ich nehme an, es geht nur um das Inhaltsverzeichnis?


Zuerst müssen wir an dem vorherigen Code noch was ändern, da ist was falsch (und nicht valide).


```
function output( toc , numbering ) {
		var list = '';
		var counter = 1;
		
		for(var k in toc) {
			if(toc.hasOwnProperty(k)) {
				list += '<li>' + getNumbering(numbering, counter) + ' ' + k;
				
				list += output( toc[k] , getNumbering(numbering, counter) );
				
				list += '</li>';
				
				counter++;
			}
		}
		
		if(list.length) {
			return '<ol style="list-style-type:none;">' + list + '</ol>';
		} else {
			return '';
		}
	}
```

Vorher waren die "ol" nicht in den "li", sondern auf der gleichen Ebene.


Und jetzt zum ein/ausklappen (hinter den Code zum Scrollen).


```
//"+" icon hinzufügen
items.filter(':has(ol)').prepend('<span class="expand" style="float:left;display:block;">+</span>');

//Alle "+" icons
var expands = $('.expand');

//Alles außer der ersten Ebene verstecken
items.find('> ol').hide();

expands.click(function(e) {
	e.stopPropagation();
	
	$('> ol', this.parentNode).slideToggle();
});
```


----------



## Linus_for_you (18. August 2011)

klasse... super...

aber jetzt klappt das Scrollen auf den unteren ebenen nicht mehr.. 
kann es sein, das das daran liegt, dass dort jetzt ein <ol> drum ist?


----------



## CPoly (18. August 2011)

Es hing damit zusammen. Jetzt wurden beim Klick auf einen tieferen Punkt auch die klick-Events der Eltern ausgelöst (http://en.wikipedia.org/wiki/DOM_events#Event_flow).

Eine Zeile behebt das


```
items.click(function(e) {
	e.stopPropagation();/*neu*/
	
	var i = items.index( this );
	var h = heading.eq( i );
	
	$(window).scrollTop( h.offset().top );
});
```


----------



## Linus_for_you (18. August 2011)

das mit dem Event Flow, leuchtet mir ein.

super klasse.. es läuft!
Prima.. bin voll happy! Danke schööön!

so, eine aller letzte kleinigkeit noch:
Wie kann ich es nun anstellen, dass 
1. die Überschriften 1. Ordnung fett gedruckt werden 
2. das "+" nach einem klick zu einem "-" wird und umgekehrt?


----------



## CPoly (18. August 2011)

1. Dafür wäre es sinnvoll, das Inhaltsverzeichnis in einen weiteren Kasten zu stecken. Dann kannst du einfach $('#kasten > ol > li'); machen und hast nur die obere Ebene.

2. Im Klick-Handler zeigt "this" auf das Element. Mittels text() kannst du den Inhalt ändern. Und mittels foo.is(':visible') prüfen, ob die Ebene darunter gerade sichtbar ist, oder nicht.


----------

