![]() | ![]() | ![]() | ![]() | ![]() |
The following example shows how validation of the nesting of HTML (or other language) tags can be performed.
The Netscape browser will not display a table (or other components) that does not have a matching end tag. This is a common cause of blank pages (or missing information). Rather than hope your comprehensive testing picks up this fact (missing information or faulty layout may not always be obvious) you can ensure that the problem is reported at development time.
Note that "NESTCHK.H" contains generic routines so it simplifies your task to "front end" some of the macros with your own. To ensure that you never forget to perform the checking its wise to always end your html with something that is required on your page such as a standard footer. The following code shows how a standard header file of yours (lets call it "MYHEADER.IH") might incorporate nesting checking (example only shows "TABLE" being validated):
;--- Include generic nesting validation header file ------------------------- #include "nestchk.h" ;--- Define what we are checking (table tags only here) --------------------- <$NestingInit Id="TABLE" DESC="HTML TABLE tags"> #define TABLE <$NestingInc Id="TABLE">TABLE #define /TABLE <$NestingDec Id="TABLE"></TABLE>
The following is a HTML page of yours that will fail the validation:
;--- Include Common header -------------------------------------------------- #include "MYHEADER.IH" ;--- Start HTML (some of this might be better in common header above!) ------ #define Title Simple html with 2 missing end of table tags <HTML> <HEAD> <TITLE><$Title></TITLE> </HEAD> <BODY> <CENTER><H1><$Title></H1></CENTER> <P>Hi there... ;--- Incorrectly nested tables follow --------------------------------------- <<$TABLE> BORDER=1> <<$TABLE> BORDER=2> <<$TABLE> BORDER=3> <$/TABLE> ;;Only "remember" to end 1 of the 3 tables! ;--- End of HTML ------------------------------------------------------------ <$StandardFooter>
The following shows part of the error details being displayed:
* Processing: E:\DB\PROJECTS\OS2\ppwizard\1.in NESTING ERRORS ~~~~~~~~~~~~~~ Missing 2 end nesting tags on "HTML TABLE tags" * line 46 of "E:\DB\PROJECTS\OS2\ppwizard\1.in" <<$TABLE> BORDER=1> * line 47 of "E:\DB\PROJECTS\OS2\ppwizard\1.in" <<$TABLE> BORDER=2>
This shows the contents of the header file (supplied with PPWIZARD) that provides the generic nesting support:
;---------------------------------------------------------------------------- ; MODULE NAME: NESTCHK.H ; ; $Author: Dennis $ ; $Revision: 1.0 $ ; $Date: 15 Dec 2000 16:52:16 $ ; $Logfile: C:/DBAREIS/Projects.PVCS/MultiOs/PPWIZARD/nestchk.h.pvcs $ ; ; REQUIRES: Regina 0.08f onwards, or you get clause length problems. ; ; DESCRIPTION: This is a header file for generic handling of ; nesting checking. This is where you must have matching ; "whatevers" such as a matching "</TABLE>" for a ; "<TABLE>" etc. ; ; "TABLE" checking is a good example as are "FRAMESET" ; statements as the Netscape browser will not display ; an incorrectly nested page (common cause of "blank" ; pages). This header file can be used to detect these ; errors while still developing the pages rather than ; HOPING that testing will show up the errors. ; ; Because these macros are generic you would usually have ; your own header file to front end them, for example your ; header could begin with: ; ; ;--- Prepare table nesting checking code --- ; #include "NESTCHK.H" ; <$NestingInit Id="HTMLTAG_TABLE" DESC="HTML TABLE tag"> ; #define TABLE \ ; <TABLE{$?}> -\ ; <$NestingInc Id="HTMLTAG_TABLE"> ; #define /TABLE \ ; </TABLE> -\ ; <$NestingDec Id="HTMLTAG_TABLE"> ; ; Your code would then always use the table macros defined ; above rather than the HTML table tags directly, example: ; ; <$TABLE BORDER=5 CELLPADDING=10> ; <TR> ;;Could validate TD /TD etc! ; ... ; </TR> ; <$/TABLE> ; ; You would probably do more that just "TABLE" tags in the ; above example! Notice that the only difference in your ; actual code is an extra '$'! ; ; Macro "HtmlNesting" ; ~~~~~~~~~~~~~~~~~~~ ; ; This header file now allows easy creation of macros such ; as the "TABLE" and "/TABLE" shown above. For example to ; create nest checking macros for "TABLE", "/TABLE", "TH", ; "/TH", "TD" and "/TD" you would use: ; ; <$HtmlNesting TAG="TABLE"> ; <$HtmlNesting TAG="TH"> ; <$HtmlNesting TAG="TD"> ; ; ; Macro "NestingInit" ; ~~~~~~~~~~~~~~~~~~~ ; ; This macro takes the following parameters: ; ; ID ; ~~ ; Manditory. Short name to describe what we wish ; to check. Must contain only characters which ; are valid in rexx variable names (no checking of ; this is done). ; ; DESC ; ~~~~ ; A description which is displayed if a nesting ; error is found. ; ; ; Macro "NestingInc" ; ~~~~~~~~~~~~~~~~~~ ; ; This macro takes the following parameters: ; ; ID ; ~~ ; Manditory. Matches that used on "NestingInit". ; You are increasing the level of nesting. ; ; ; Macro "NestingDec" ; ~~~~~~~~~~~~~~~~~~ ; ; This macro takes the following parameters: ; ; ID ; ~~ ; Manditory. Matches that used on "NestingInit". ; You are decreasing the level of nesting. ; ; ; Macro "NestingLevel" ; ~~~~~~~~~~~~~~~~~~~ ; ; This macro takes the following parameters: ; ; ID ; ~~ ; Manditory. Matches that used on "NestingInit". ; Returns the name of the variable which contains ; the level (0= neutral, 1 = down one etc). ; ;---------------------------------------------------------------------------- ;--- Only include this header once! ----------------------------------------- #ifndef VERSION_NESTCHK_H ;--- Define the version number of this header file -------------------------- #define VERSION_NESTCHK_H 99.317 #require 99.317 ;--- Register checking macro ------------------------------------------------ #OnExit <$NestingCheck> ;--- Currently not tracking nesting of anything ----------------------------- #RexxVar NestingIdCount = 0 ;--- Define macro to keep track of items ------------------------------------ #DefineRexx NestingInit_REXX ;--- Ensure ID is valid (needs to be valid rexx var format) ----- if symbol('{$ID $$SQx2}') = 'BAD' then Error('The ID of "{$ID $$SQx2}" contains invalid characters!', 'It must be in the format of a valid rexx variable name'); if symbol('{$ID $$SQx2}_Level') = 'VAR' then Error('The ID of "{$ID $$SQx2}" has already been initialized!'); ;--- Remember ID details -------------------------- NestingIdCount = NestingIdCount + 1; call _valueS "NESTINGID.NestingIdCount.ID", '{$ID $$SQx2}'; call _valueS "NESTINGID.NestingIdCount.DESC", '{$DESC}'; call _valueS "{$ID $$SQx2}_DESC", '{$DESC}'; ;--- Initialize level counter --------------------- call _valueS '{$ID $$SQx2}_Level', 0; #DefineRexx #define NestingInit \ #evaluate '' ^<$NestingInit_REXX ID='{$ID}' DESC='{$DESC}'>^ ;--- We are going down one level of nesting --------------------------------- ; [SaveSourceAndLocnInfo] (Ignore this - this bit used to include section into doco) #DefineRexx NestingInc_REXX ;--- Ensure ID is valid ------- if symbol('{$ID $$SQx2}_Level') <> 'VAR' then Error('The ID of "{$ID $$SQx2}" is unknown (you must use "NestingInit")!'); ;--- Increase nesting level --- call _valueS "{$ID}_Level", {$ID}_Level + 1; ;--- Record details about current location --- call _valueS "{$ID}_FilePosn.{$ID}_Level", GetInputFileNameAndLine(); call _valueS "{$ID}_CurrLine.{$ID}_Level", GetFileLineBeingProcessed(); #DefineRexx #define NestingInc \ #evaluate '' ^<$NestingInc_REXX ID='{$ID}'>^ ; [SaveSourceAndLocnInfo] (Ignore this - this bit used to include section into doco) ;--- We are going up one level of nesting ----------------------------------- #DefineRexx NestingDec_REXX ;--- Ensure ID is valid ------- if symbol('{$ID $$SQx2}_Level') <> 'VAR' then Error('The ID of "{$ID $$SQx2}" is unknown (you must use "NestingInit")!'); ;--- Decrease nesting level --- NewLevel = {$ID}_Level - {$DecreaseBy="1"}; if NewLevel < 0 then call Error "Too many end tags for " || {$ID}_DESC; ;--- Record updated details --- call _valueS "{$ID}_Level", NewLevel; #DefineRexx #define NestingDec \ #evaluate '' ^<$NestingDec_REXX ID='{$ID}' {$?}>^ ;--- Allow user to access the counter variable ------------------------------ #define NestingLevel \ {$ID}_Level ;--- Check nesting of all ID's ---------------------------------------------- #DefineRexx NestingCheck_REXX ;--- Loop though all registered "nesting" items --- NestingError = 0; ;;No errors found yet do CheckIndex = 1 to NestingIdCount; ;--- Get basic information about item --- _ID = NESTINGID.CheckIndex.ID; _DESC = NESTINGID.CheckIndex.DESC; _Level = _valueG(_ID || "_Level"); if _Level <> 0 then; do; if NestingError = 0 then; do; say ''; say 'NESTING ERRORS'; say '~~~~~~~~~~~~~~'; end; NestingError = NestingError + 1; if _Level < 0 then; say 'Have ' || abs(_Level) || ' too many end nesting tags on "' || _DESC || '"'; else; do; ;--- display nesting info --- say 'Missing ' || _Level || ' end nesting tag(s) on "' || _DESC || '"'; do _ThisLevel = 1 to _Level; say ' * ' || _valueG(_ID || "_FilePosn._ThisLevel"); say ' ' || _valueG(_ID || "_CurrLine._ThisLevel"); end; end; end; end; #DefineRexx #define NestingCheck \ #evaluate '' ^<$NestingCheck_REXX>^ \ #if [NestingError <> 0] \ #error ^NestingCheck(): Found <??NestingError> nesting error(s) as described above^ \ #endif ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ;%%%[ The following macros allow you to easily define HTML tag Nesting ]%%% ;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ;--- This is the only tag of the following you should use! ---------------- #define HtmlNesting \ ;--- Initialization for this tag --- -\ <$NestingInit Id="HTML_{$TAG}" \ DESC='HTML {$TAG} tag nesting' -\ > -\ -\ #evaluate ^^ ^<$REXX_HTML_NESTING TAG=^{$TAG}^ Type="Inc">^ -\ #evaluate ^^ ^<$REXX_HTML_NESTING TAG=^{$TAG}^ Type="Dec">^ ;--- Define GENERIC macros for TAG and /TAG (use pseudo parameters) ------- #define _HTML_NESTING_Inc \ [Tag]{$?}> -\ <$NestingInc Id="HTML_[Tag]"> #define _HTML_NESTING_Dec \ </[Tag]{$?}> -\ <$NestingDec Id="HTML_[Tag]"> ;--- Define code which creates TAG start/end macros ----------------------- #DefineRexx REXX_HTML_NESTING ;--- Get GENERIC Macro (START or END) --- TagMacro = MacroGet('_HTML_NESTING_{$Type}'); ;--- Replace pseudo parameters --- TagMacro = ReplaceString(TagMacro, '[Tag]', '{$TAG $$SQx2}'); ;--- Create START or END html tag macro --- if '{$Type}' = 'Inc' then HtmlTag = '{$TAG $$SQx2}'; else HtmlTag = '/{$TAG $$SQx2}'; call MacroSet HtmlTag, TagMacro; #DefineRexx ;--- Only include this header once! ----------------------------------------- #endif
![]() | ![]() | ![]() | ![]() | ![]() |