Alternative Widgets logo
Home icon

Alternative Widgets

Welcome to Alternative Widgets! This site is an alternative to popular code libraries.

Unlike JQuery, Bootstrap, and similar plug-ins, these are easy to alter, have a bare minimum of code, and wrap any content.

You do not have to know JavaScript to use these widgets, but you should be comfortable with HTML and CSS so that you can alter the styles to suit your needs.

Explore the widgets below. You will find a demo, HTML, CSS, and JavaScript for each.

Hide/Reveal

Purpose:

Click on an element to reveal some content, and click again to hide it. This is useful for question/answer situations or just to contain large content until called upon.

To apply:

  1. Add this to the opening tag of the element that a user should click to hide/reveal some content:
    onclick="reveal('yourID');"
  2. Add the style cursor: pointer; to the above element so users know it's clickable.
  3. Wrap the content to be hidden/revealed in a div with the class "hidden" and add an ID which matches the ID in the function call above. The IDs should be unique for each hidden/onclick pair:
    <div class="hidden" id="yourID">

Example:


Darth Vader!

Home

Modals

Purpose:

Create a pop-up and gray out the page behind it.

Please note this widget is meant to allow any element to open a widget that can contain any number of other elements. If you want a simple image pop-out, use the Image Pop-Out Modals widget instead.

To apply:

  1. Wrap your pop-up's content in a div with the class "modalContent".
  2. You can include an optional closing button at the top or bottom of the modalContent div, like in the example. (The modal will also close if the gray background is clicked.)
  3. Wrap each "modalContent" div in a parent div with the class "modalBackground".
  4. Apply a unique ID to each of the "modalBackground" divs.
  5. Apply this onclick statement to the opening tag of the element that a user should click to call the modal. Your modalBackground ID should be in the function's parameter.
    onclick="openModal('myModalID');"

Optional:

Zoom-In Effect: By default the modals open with a zoom-in effect. If you don't like it just remove the line animation: modalZoomIn .25s; from the "modalContent" class in the CSS.

Fade-In Effect: The modal can open with a subtle fade-in effect instead. To apply, replace the zoom-in line above with animation: modalFadeIn .25s; on the "modalContent" class in the CSS.

Additional Close Button: If you want to create an additional button that closes the modal within the modal's content, apply style = "cursor: pointer;" and onclick="closeModal();" to the opening tag of the element used as the button.

Example:


Home

Image Pop-Out Modals

Purpose:

Create a pop-out for images.

Please note this widget is meant to pop out images only. If you want a pop-out modal that can contain any content, use the Modals widget instead.

To apply:

  1. Drop this hidden image modal container anywhere on your page:
    <div id="imageModalBackground" onclick="backgroundClick(event);">
      <div id="imageModalClose">&#x2715;</div>
      <img alt="Image in modal." src="data:," id="currentImage" />
    </div><!-- End imageModalBackground. -->
  2. Apply the part in red to any image you want to pop out. You do not need to wrap the image in an <a> tag.
    <img src="contents/images/pear.png" alt="Pear" class="modalImage" onclick="showImageModal(this.src);" />

The imageModalClose button is not required because the modal will close when the background is clicked. It's just a more obvious exit.

Optional:

Captions: You can pull page content into the modal to serve as a caption.

  1. Add a unique ID to the paragraph you want to use as a caption. If you have multiple elements, wrap them in a div with a unique ID.
  2. Add the line in red to the image modal container. This line will contain the caption.
    <div id="imageModalBackground" onclick="backgroundClick(event);">
      <div id="imageModalClose">&#x2715;</div>
      <img alt="Image in modal." src="data:," id="currentImage" />
      <div id="imageCaptionContainer"></div>
    </div><!-- End imageModalBackground. -->
  3. Apply the part in red to any image you want to pop out. The argument in the showCaption() function call (caption1 in the example) must match the ID of the caption in step 1.
    <img src="contents/images/pear.png" alt="Pear" class="modalImage" onclick="showImageModal(this.src), showCaption('caption1');" />

Zoom-In Effect: By default the image and caption open with a zoom-in effect. If you don't like it, remove the line animation: imageModalZoomIn .25s; from both the "imageModalBackground img" and "imageCaptionContainer" IDs in the CSS.

Fade-In Effect: The image and caption can open with a subtle fade-in effect instead. If you prefer that, replace the line animation: imageModalZoomIn .25s; with animation: imageModalFadeIn .25s; for both the "imageModalBackground img" and "imageCaptionContainer" IDs in the CSS.

Example:


Apple Orange Pear
Home

Replace Contents

Purpose:

Replace content with a click.

This simple widget has no menu, unlike other widgets on this site. It can also take any content, not just images like in the example.

To apply:

  1. Wrap each of your content blocks in a div with the class "replaceableContent".
    • These will not display on page load. If you want one to display on load (like the example below) add the style "display: block;" to its opening tag.
  2. Add a unique ID to each replaceableContent div's opening tag.
  3. Add the class "replaceContentButton" to whatever element will serve as the button which reveals one of the hidden divs.
  4. Add an onclick statement to each button which calls the replaceContent() function, and passes the ID of the hidden content as a parameter, like this:
    onclick="replaceContent('yourID');"

Please note, the JavaScript for this widget must be placed after the related content. It can be embedded in a script tag or referenced externally. But it will not work if referenced above the content.

Example:


Click the word bananas to replace the apple below. Click the word cherry to see a cherry. And click the word apple to restore the apple.

apple
bananas
cherry
Home

Load More

Purpose:

This tool adds hidden content to a page, one segment at a time. It is useful for long pages, especially when the content is laid out in rows or lists.

To apply:

  1. Wrap each hidden segment in a div with the class "loadContent".
  2. To create the "Load More" button, place this div above each loadContent div:
    <div class="loadButton" onclick="loadMore();" >Load More</div>
  3. Add the class "firstLoadButton" to only the first instance of the loadButton div, like this:
    <div class="loadButton firstLoadButton" onclick="loadMore();">Load More</div>

Example:


1
2
3
4
5

Load More

6
7
8
9
10

Load More

11
12
13
14
15

Load More

16
17
18
19
20

Home

Reveal by Scroll

Purpose:

Reveal content as a user scrolls down the page.

To apply:

Wrap each block to be revealed in a div with the class "scrollBlock". (The first block will be displayed on page load.) When the bottom of the last displayed block enters the screen frame, the next block is revealed.

Please note, the JavaScript for this widget must be placed after the related content. It can be embedded in a script tag or referenced externally. But it will not work if referenced above the content.

Example:

Each image below is contained in a scrollBlock which appears as you scroll down. Notice the scroll bar at the right of your browser adjust as the blocks are added to the screen. If you missed it the first time, just refresh the browser.

apple
orange
pear
bananas
cherry
grapes
peach
strawberry
blackberry
rasberry
pineapple
Home

Accordion

Purpose:

Contain numerous sections in a small area, and make each quickly accessible on one page. When one opens, the others close.

To apply:

  1. Wrap the text to be used as buttons in divs with the class "accordionHeader".
  2. Wrap the hidden sections in divs with the class "accordionSection", and apply a unique ID to each opening tag.
    • The accordionSection divs must always follow their corresponding accordionHeader divs. There must be nothing in between. But anything can go within each.
    • The header/section pairs function independently of others and do not have to be adjacent to others.
  3. Add an onclick to the selectAccordionSection function to each header. It should look like this:
    onclick="selectAccordionSection(this.nextElementSibling.id);
  4. Optional: Add a + icon in each header, which becomes - when the section is open. To apply, just place this after the opening tag of each accordionHeader:
    <div class="headerIcon"></div>
  5. Optional: Add a "View All" option which opens all accordionSections. You can apply the onclick to any element, anwhere on the page. It should have a "cursor: pointer;" style and call the viewAll() function. For example:
    <p style="cursor: pointer;" onclick="viewAll();">View All</p>.

Please note, the JavaScript for this widget must be referenced after the content to which it applies. It can be embedded or referenced externally.

You might have multiple accordions on your page. If a user opens a page on accordion1, then opening a page on accordion2 will make open pages in other accordions close. If that will be a problem, there are additional steps that will fix it, but I made them separate to make the basic coding as simple as possible. Click below to get the multiple accordion solution.

To make multiple accordions function independently of each other:

  1. Wrap each accordion in a div with a unique ID, like "firstAccordion" for example.
  2. Use this JavaScript file instead.
  3. Use this HTML as a reference instead.
  4. Optional: If you're using the View All option on any accordion, you have to call the viewAllSectionsInThisAccordion() function instead, and pass the accordion ID as a parameter. For example:
    <p style="cursor: pointer;" onclick="viewAllSectionsInThisAccordion('firstAccordion');">View All</p>.

Example:


Section 1 Header
apple

Praesent condimentum interdum elit iaculis laoreet. Vestibulum non molestie orci. Proin quis justo quam. Morbi tempus quis elit nec scelerisque. Quisque hendrerit orci enim. Pellentesque eget hendrerit augue. Nulla accumsan turpis at diam cursus varius. Ut sem ipsum, eleifend sit amet egestas a, volutpat sed augue. Aenean a risus interdum metus tincidunt imperdiet in quis erat. Proin tincidunt iaculis efficitur. Sed molestie lectus vel quam convallis tempor sed vel eros. Ut interdum sem a rutrum venenatis. Vivamus sit amet dapibus sem. Etiam in ultricies dui. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nullam non velit nec nisi aliquet consequat.

Fusce et leo nec est imperdiet facilisis a in elit. Pellentesque pellentesque metus ut consectetur euismod. Vestibulum molestie augue id accumsan vestibulum. Suspendisse potenti. Sed nec facilisis metus, eget tincidunt magna. Donec odio augue, ultrices ut suscipit non, consequat non massa. Vestibulum molestie id sem quis auctor. Nunc egestas mi sed nunc imperdiet, et luctus nulla congue. Aliquam erat volutpat. Donec risus urna, vulputate eu orci in, ultricies elementum metus. Praesent non euismod diam. Nullam varius rutrum magna in auctor. Morbi mattis dolor et diam vulputate accumsan. Sed vel nisl maximus, pretium dolor nec, commodo sapien.

Section 2 Header
bananas

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam vel consectetur metus, nec maximus dolor. Cras sapien diam, ultricies ut volutpat sed, fringilla quis nunc. Sed sed risus lorem. Aenean non sapien vitae leo lobortis eleifend sit amet vel lectus. Pellentesque porta ligula eu dignissim lacinia. Praesent sed posuere turpis, in semper nunc. Nam consectetur, leo quis fermentum euismod, neque nibh sagittis arcu, at consectetur nibh felis tempor quam. Curabitur viverra posuere nisl, non venenatis eros imperdiet a. Fusce eu metus nibh. Donec id dolor dolor.

Etiam condimentum rutrum faucibus. Suspendisse sed accumsan quam, a vehicula enim. Pellentesque posuere augue quis nunc condimentum, quis pulvinar purus sollicitudin. Sed orci mauris, hendrerit a pellentesque eu, venenatis at sapien. Vestibulum vel ultricies nisl. Maecenas vitae est elementum tellus maximus iaculis. Vestibulum fringilla porta interdum. Aliquam dictum, lorem vitae tincidunt semper, lectus lorem semper ipsum, quis rhoncus nisi leo ullamcorper libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer vehicula ullamcorper laoreet. Duis eget neque a nisi faucibus malesuada. Donec quis est molestie, tempor dui sit amet, suscipit arcu. Nullam eget feugiat leo, ac porta felis. Donec hendrerit purus et auctor tempor. Nunc commodo, dolor vitae elementum convallis, est eros vulputate ipsum, sit amet mollis nisi est id risus. Morbi egestas a tellus eget viverra.

Section 3 Header
cherry

In dui augue, molestie id feugiat sit amet, ultricies eget felis. Nulla pharetra pulvinar urna, sit amet semper dolor maximus sed. Donec congue magna placerat quam ullamcorper sodales vel sit amet lorem. Pellentesque id massa at velit convallis porta. Proin auctor neque tellus, eget sagittis nulla vehicula a. Etiam orci eros, aliquet imperdiet ligula ultricies, viverra vehicula sem. Duis blandit pulvinar lorem, in viverra felis porta id.

Praesent non est consectetur, aliquet sem vitae, aliquet ipsum. Praesent in augue ut massa lacinia ullamcorper quis ac purus. Quisque nec suscipit nisi. Nulla facilisi. Integer arcu urna, fringilla id suscipit sed, fringilla non ex. Phasellus mollis ante vel gravida semper. Morbi in vestibulum felis, varius pharetra orci. Aliquam nunc lorem, suscipit ac interdum quis, placerat eu tellus. Nulla ut euismod purus, ac fringilla purus. Ut lobortis nec sapien lacinia porttitor. Nulla facilisi. Praesent metus sem, imperdiet ut euismod a, interdum ut neque. Fusce tristique euismod dapibus. Nulla efficitur bibendum placerat. Etiam ut ligula et nibh euismod malesuada at nec est.

Section 4 Header
testing

Integer ut vehicula erat. Aenean volutpat nisl sem, vitae sagittis felis auctor eu. Nam mattis elit eget purus suscipit dapibus. Pellentesque maximus molestie feugiat. Sed volutpat, lorem sit amet consectetur vulputate, quam orci molestie urna, ac dignissim est magna id nisi. Mauris fringilla consequat leo, sed laoreet nibh laoreet non. Etiam at mauris dapibus, euismod nisi nec, fringilla ex. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum aliquet arcu id sagittis aliquet. Vivamus dui leo, imperdiet vitae eros non, molestie ultricies lacus. Maecenas placerat orci eget nisl ultricies suscipit.

Proin mollis mi at est luctus porttitor. Aliquam erat volutpat. Curabitur in lectus eu sem consectetur molestie. Vivamus libero lacus, euismod id quam ac, fermentum eleifend mi. Maecenas viverra ligula at venenatis mattis. Maecenas rhoncus tincidunt vestibulum. Nulla facilisi. Morbi eu eros eget augue vehicula eleifend eu id mi. Curabitur ullamcorper ipsum maximus mauris tristique, a pulvinar sem aliquam. Nulla quis fermentum mauris. Praesent nec ipsum vel justo finibus ullamcorper varius nec sem. Nullam mollis, dui at commodo porta, enim velit posuere erat, a congue ipsum nibh et odio. Aliquam quam est, cursus venenatis nibh ut, cursus dapibus libero. Integer ultricies, dui ac tempus porttitor, nibh magna faucibus metus, vitae ullamcorper ipsum ipsum sit amet nisl.

Home

Back/Next Pagination

Purpose:

Load pages on click with the back and next buttons. The sections can take any content, not just images. Use them for entire pages, or just place images within for a gallery.

To apply:

  1. Wrap each page in a div with the class "fullPage". The first one will be displayed on page load.
  2. Copy over the back and next buttons' code from the example, and place them above the fullPage div.
  3. Wrap all the fullPages and the buttons in a div with the class "allPagesContainer".

The back and next buttons will display above the page's contents, but they are transparent until hovered over.

Example:



This is Page 1

testing

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.

This is Page 2

testing

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

This is Page 3

It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).

This is Page 4

There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.

This is Page 5

The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Home

Pagination with Counter

Purpose:

Load pages on click with the back and next buttons, while a page counter updates. The sections can take any content, not just images. Use them for entire pages, or just place images within for a gallery.

To apply:

  1. Wrap each page in a div with the class "pageWithCounter".
  2. Wrap all the pages in a div with the class "allPagesWithCounter".
  3. Copy over the "counterDashboard" div from the HTML example and place it above the "allPagesWithCounter" div. The dashboard contains the back and next buttons and the counter.

Please note, the JavaScript for this widget must be placed after the related gallery content in a script tag. It can be embedded in a script tag or referenced externally. But it will not work if referenced above the content.

Example:



apple
orange
grapes
pineapple
bananas
testing

Example of page-wide mixed content.

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.

The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.

Home

Rotating Content

Purpose:

Create pages which rotate automatically. The pages can take any content, not just images

To apply:

  1. Wrap your pages in divs with the class "rotatingPage".
  2. Place all the rotatingPage divs in a div with the class "rotatingPagesContainer".
  3. Set your rotation interval in the "rotationInterval" variable, which for convenience is the first item in this widget's JavaScript. The time must be in millisenconds.

Please note, the JavaScript for this widget must be referenced after the content to which it applies. It can be embedded or referenced externally. The first page will not load until the first interval if the script comes before the content.

Example:


apple
orange
grapes
pineapple
bananas
Home

Rotating Content with Stops

Purpose:

Create rotating pages with stops. When a stop is clicked, the rotation ends.

To apply:

  1. Wrap your sections in divs with the class "pageWithStops".
  2. Place all the pageWithStops divs within a div with the class "pagesWithStopsContainer".
  3. Place this above the pagesWithStopsContainer. It houses the buttons:
    <div id="rotatorButtonContainer" onclick="stopRotationGalleryWithStops();"></div>
  4. Set your rotation interval in the "rotateFrequency" variable, which for convenience is the first item in this widget's JavaScript. The time must be in millisenconds.

Please note, the JavaScript for this widget must be referenced after the content to which it applies. It can be embedded or referenced externally. But the widget will not work if the script comes before the content.

The sections can take any content, not just images. So this tool can be used for entire pages.



apple
orange
grapes
pineapple
bananas
Home

Horizontal Pagination

Purpose:

This widget creates a page selector with a horizontal menu, which becomes a drop menu on narrow screens.

If you are using a mobile device, or if you undock and narrow down your browser, you should see the drop menu instead.

There is also a vertical menu version.

To apply:

  1. Wrap each of your pages in a div with the class "page" and apply a unique ID to each.
  2. Create a div with the class "pageMenu" and place it just above your first page. This will be the horizontal menu bar which spans the page.
  3. Add a call to the hideResponsiveDropMenu() function to the opening tag of the pageMenu div, like this:
    <div class="pageMenu" onclick="hideResponsiveDropMenu();">
  4. Create the individual buttons within the pageMenu div. Each should be a div with the class "pageButton" and a unique ID.
  5. For each button, add a call to the displayPage() function which takes the page ID and button ID as parameters. So your buttons should look like this:
    <div class="pageButton" id="pageButton1" onclick="displayPage('page1', 'pageButton1');">Page 1</div>
  6. Copy over the entire horizontalMenuIconContainer div from the example, and place it above the pageMenu div.
  7. Wrap all of the above in a div with the class "pagesContainer".

Please note, the JavaScript for this widget must be referenced after the content to which it applies. It can be embedded or referenced externally. But the widget will not work if the script comes before the content.

By default the horizontal menu is replaced by the drop menu at a screen width of 500px. This should account for most phones at vertical orientation. If you want to change the width at which the drop menu is triggered, follow the steps below.

  1. In the JavaScript, the width at which the drop menu is triggered is contained in the variable dropMenuModeWidth at the top. You only have to change it here.
  2. In the CSS, the width must be changed in several media queries, and can't be applied via variable. So you just have to search and replace each instance of the current width.

You might have multiple horizontal pagination widgets on your page. If a user opens a page on one, then opening a page on another will cause all open pages in other horizontal pagination widgets to close. These additional steps that will fix this. I made them separate to make the basic coding as simple as possible.

  1. Make sure each page and pageButton div still has a unique ID even if they are in different pageContainers.
  2. Add a unique ID to each pagesContainer div tag, like "pagesContainer1" for example.
  3. Apply the pagesContainer ID as a third parameter to each of the the pageButton function calls, like this:
    onclick="displayPage('page1', 'pageButton1', 'pagesContainer1');"
  4. For each pageMenu, pass the ID of the parent pagesContainer as a parameter of the hideResponsiveDropMenu function, like this:
    <div class="pageMenu" onclick="hideResponsiveDropMenu('pagesContainer1');">
  5. Use this JavaScript instead.
  6. Reference this HTML instead.

Example:


Page 1: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Page 2: Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?

Page 3: It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).

Page 4: There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.

Page 5: The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Home

Vertical Pagination

Purpose:

This widget creates a multi-page selector with a vertical menu which is set to the left of the pages.

There is also a horizontal version of the menu.

To apply:

  1. Wrap each of your pages in a div with the class "verticalPage" and apply a unique ID to each.
  2. Wrap each button in a div with the class "verticalPageButton" and apply a unique ID to each.
  3. Each button needs an onclick which calls the displayVerticalPages function, and passes both page ID and button ID as parameters, like this:
    onclick="displayVerticalPage('verticalPage1', 'verticalPageButton1');"
  4. Wrap all the buttons in a div with the class "verticalMenuContainer". Place this above the first verticalPage.
  5. Wrap all of the above in a div with the class "verticalPaginationContainer".
  6. You may find that you need to increase the button widths if their text is long. If you do, you'll have to decrease the page widths accordingly. This will have to be trial and error to suit your needs.

Please note, the JavaScript for this widget must be referenced after the content to which it applies. It can be embedded or referenced externally. But the widget will not work if the script comes before the content.

Caution: This kind of menu does not look good on narrow screens because the pages will become too narrow. Consider using a drop menu instead.

You might have multiple vertical pagination widgets on your page. If a user opens a page on one, all open pages in other vertical pagination widgets will close.

To make multiple vertical pagination widgets function independently of each other:

  1. Add a unique ID to each verticalPaginationContainer div, like "verticalPagesContainerA".
  2. Apply the ID as a third parameter to each of the the function calls within the widget's segments, like this:
    onclick="displayVerticalPage('verticalPageA', 'verticalPageButtonA', 'verticalPagesContainerA');"
  3. Reference this HTML example instead..
  4. Use this JavaScript file instead.

Example:


Page 1
Page 2
Page 3
Page 4
Page 5

This is Page 1

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

This is Page 2

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?

This is Page 3

It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).

This is Page 4

There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.

This is Page 5

The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Home

Countdown

Purpose:

Create a countdown clock to alert users to deadlines, or promote upcoming events.

To apply:

  1. Create an HTML element to hold the timer. This is the only HTML necessary for the tool. It must have a "countdownText" ID, like this:
    <p id="countdownText"></p>
  2. Change the date and time to which you are counting down. You will find them in the first variable in the countdown.js file.
    var countdownDate = new Date("February 28, 2018 23:59:00").getTime();
    Please note the date and time must be in the above format.
  3. Change the text that will replace the timer if the countdown has ended. You will find it in the second variable in the countdown.js file.
    var displayAfterCountdown = "The countdown has ended.";

The countdown is triggered on page load.
There is no CSS necessary for this tool. Style the "countdownText" container as you see fit.

Please note, the user-side time entry field below is just for this demonstration. In the tool's code, you must enter the time in the script's variable.

Example:


Enter a new date and time below with the same format, and click submit to see the countdown change.
Enter a past date to verify that your displayAfterCountdown message displays.

Home

Timed Content

Purpose:

Automatically change content, or just reveal content, at a specific point in time.

To apply:

  1. Create an HTML element to hold the original content. Apply the class "contentBefore" and the style "display: block;" like this:
    <div class="contentBefore" style="display: block;"></div>
    • If you only want to reveal content but have no original content, the above element is not required.
    • You can have multiple contentBefore elements.
    • You can also set the style to "inline-block" or "inline", but you will have to find and update the JavaScript display command to match.
  2. Create an HTML element to hold the content that will be revealed after the time target. Apply the class "contentAfter" with a "display: none;" style like this:
    <div class="contentAfter" style="display: none;"></div>
    • If you only want to make the original content disappear, this element is not necessary.
    • You can have multiple contentAfter elements.
    • If you want contentAfter to occupy the same space as contentBefore, it must be adjacent in the code.
  3. Change the date and time at which the content changes. You will find them in the first variable in the timed-content.js file.
    var timeTarget = new Date("January 1, 2025 23:59:00").getTime();
    Please note the date and time must be in the above format.

There is no CSS necessary for this widget beyond what has been mentioned above.

Please note, the JavaScript for this widget must be placed after the related content. It can be embedded in a script tag or referenced externally. But it will not work if referenced above the content.

Also, the user-side time entry field below is just for this demonstration. In the widget's code, you must enter the time in the variable.

Example:



Enter a new date and time below with the same format, and click submit to see the paragraph below change.



This is content BEFORE timeTarget.
Home

Responsive Design

Purpose:

Create content which changes to accomodate device width via media queries.

This is really just an explanation, not a widget. I have encountered a lot of unnecessarily complicated descriptions of responsive design online. Basically it's about making your page display differently based on screen widths. There are three ways to achieve this:

  1. Create two completely different pages, one for wide screens and one for mobile screens, and place the mobile one inside a folder called "Mobile" or just "M" on your server space's main folder. Mobile devices will look there first for an index or homepage. Usually. They may not. And you're creating two different pages.
  2. Create two different pages, and add a JavaScript event listener which checks for screen widths, or screen width changes (in case someone resizes their browser window, or places their mobile device sideways). The script will redirect to the page designed for each width. But you're still creating two pages.
  3. Add media queries to your CSS classes. The queries contain styles which become active based on screen width. This is optimal since it requires no programming, and you're doing everything on one page.

The third version is used in this example, and throughout this site. Media query explanations are also often overly complicated. The only version you have to worry about in this context is the screen width query below.

To apply:

This is a media query.
@media screen and (max-width: 720px){
  YOUR CLASS HERE
}

  1. "Max-width" is the screen width at which this query's styles become active. Set it to any width you like. (720 is broadly used for tablets, and 420 for phones, but there is really no "standard" - it's trial and error.)
  2. Copy your CSS class (with its opening and closing tags) into this query's tags. Change the styles as you need them to look at this screen width and smaller. You can create as many queries with varying screen widths as you need for each class.

If you're on a desktop or laptop, undock and narrow your browser. You will see the following changes in the example below:

A great way to make divs responsive is to change their widths, and also display style from "inline-block" to "block" on narrow screens. They will sit side-by-side on wide screens and atop each other on narrow screens.

Example:



Laptop width
Bananas.
Tablet width
Orange.
Phone width
Grapes.
Home

Responsive Asides

Purpose:

Create asides which float to the left or right of content, and which expand to full width on narrow screens. The code provides for images and captions within the asides. The code also provides a full width image and caption option.

To apply:

  1. Wrap the content you want floated in a div with the class "asideLeft" or "asideRight".
  2. Change the div's width to your needs.
  3. You might need to change the screen width within the media query. This is the screen width at which the floated div becomes full width.
  4. Optional: The CSS makes any paragraph within the aside display with smaller gray text to act as an image caption. If you don't like that just remove the styles.
  5. Optional: If you want the image and caption styles to apply to full width images, wrap them in a div with the class "fullWidthDiv".

Example:

Test the responsiveness of the asides by simply undocking your browser and narrowing it down. If you are on a phone they will already be full width. The last example is already full width, but demonstrates you can use it for full width images and captions.

Apple

Try adding a little salt on sliced apples for a sweet and savory snack.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?

bananas

Try spreading strawberry jam on bananas for a quick snack.

It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Integer ut vehicula erat. Aenean volutpat nisl sem, vitae sagittis felis auctor eu. Nam mattis elit eget purus suscipit dapibus. Pellentesque maximus molestie feugiat. Sed volutpat, lorem sit amet consectetur vulputate, quam orci molestie urna, ac dignissim est magna id nisi. Mauris fringilla consequat leo, sed laoreet nibh laoreet non. Etiam at mauris dapibus, euismod nisi nec, fringilla ex. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum aliquet arcu id sagittis aliquet. Vivamus dui leo, imperdiet vitae eros non, molestie ultricies lacus. Maecenas placerat orci eget nisl ultricies suscipit.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Pears

Do you prefer yellow or brown pears?

Proin mollis mi at est luctus porttitor. Aliquam erat volutpat. Curabitur in lectus eu sem consectetur molestie. Vivamus libero lacus, euismod id quam ac, fermentum eleifend mi. Maecenas viverra ligula at venenatis mattis. Maecenas rhoncus tincidunt vestibulum. Nulla facilisi. Morbi eu eros eget augue vehicula eleifend eu id mi. Curabitur ullamcorper ipsum maximus mauris tristique, a pulvinar sem aliquam. Nulla quis fermentum mauris. Praesent nec ipsum vel justo finibus ullamcorper varius nec sem. Nullam mollis, dui at commodo porta, enim velit posuere erat, a congue ipsum nibh et odio. Aliquam quam est, cursus venenatis nibh ut, cursus dapibus libero. Integer ultricies, dui ac tempus porttitor, nibh magna faucibus metus, vitae ullamcorper ipsum ipsum sit amet nisl.

Home

Responsive Columns

Purpose:

Separate content into columns, which become stacked and page-wide on narrow screens.

To apply:

  1. Wrap each column's content in a div with the class "columnContainer".
  2. In the CSS, add a media query block for each screen width at which the column count changes. (The provided CSS has two.) For example, if you want all columns to become one stack at phone width, you only need one media query block. If you want four columns to become two at tablet width, and then one at phone width, you need two media query blocks.
  3. Within each media query block, change the point at which the columns count changes. That would the the width in the line @media screen and (max-width: 720px). Usually 720px is good for tablet size and 450px works for phone size, but adjust it for your needs.
  4. Change the widths and margins in the columnContainer class to suit your needs. Reduce the widths by 3 or 4 percent to account for margins and possible parent container padding. Therefore:
    • 4 columns: width = 21%
    • 3 columns: width = 30%
    • 2 columns: width = 46%
    • 1 column: width = auto
  5. Optional: If want the columns centered on the page, wrap them all in a div with the style text-align: center;. You would then have to add "text-align: left;" on the columnContainer class, if you want them that way. The example below uses this option.

Example:

The example below has two media query blocks, allowing the four columns at laptop width to become two at tablet width, and then one at phone width. Narrow down your browser to see the columns respond.

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy.
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source.
There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text.
Home

Responsive Table

Purpose:

Create a responsive table in which font and padding sizes are reduced for mobile devices. Rows and columns are styled without any inline styles.

To apply:

  1. Apply the class "myTable" to your table's opening tag.
  2. Change the style colors and widths to suit your needs in the CSS only. You do not have to add inline styles to any table elements. See below on how to target specific rows, columns, or cells via CSS.
  3. Wrap your table in a div with the class "tableContainer". This centers the table, and more importantly creates a sideways scroller under your table only, as opposed to your whole page, if your table overflows your page's width.

Please note: The order of CSS selectors for table styles is important. The styles which affect the most specific rows or columns should go last.

Although this table is responsive, too many columns with too much data will of course always overflow a narrow screen. Use tables for simple data, not for page structure or large content.


Optional Table Style Selectors

Click the button below to see some CSS selectors which are useful in targeting specific table elements.

Every other row
You can insert either "odd" or "even" in the parentheses.
.myTable tr:nth-child(odd) td

Specific columns
This affects only the column number in parentheses.
.myTable td:nth-child(1)

Specific row
This affects only the row number in parentheses.
.myTable tr:nth-child(1) td

Range of columns
Replace x and y with integers which represent the first and last columns to be affected.
.myTable td:nth-child(n+x):nth-child(-n+y)

Range of rows
Replace x and y with integers which represent the first and last rows to be affected.
.myTable tr:nth-child(n+x):nth-child(-n+y) td

Every nth row
Replace x with an integer representing the nth row. For example, 4n would affect every 4th row.
.myTable tr:nth-child(Xn) td

Every nth column
Replace x with an integer representing the nth column. For example, 4n would affect every 4th column.
.myTable td:nth-child(Xn)

Specific column to last column
Replace x with an integer representing a starting column. All subsequent columns will be affected.
.myTable td:nth-last-child(-n+X)

Specific row to last row
Replace x with an integer representing a starting row. All subsequent rows will be affected.
.myTable tr:nth-last-child(-n+X) td

Example:


PLANET DISTANCE FROM SUN DIAMETER REVOLUTION ROTATION Voyager Fly-by
Mercury 57.91 million km 4,879 km 88 days 59 days n/a
Venus 108.2 million km 12,104 km 224.7 days 243 days n/a
Earth 149.6 million km 12,742 km 365.26 days 23.93 hours n/a
Mars 227.9 million km 6,779 km 1.88 years 24.6 hours n/a
Jupiter 778.5 million km 139,822 km 11.86 years 9.8 hours V1 1979
V2 1979
Saturn 1.433 billion km 116,464 km 29.7 years 10.2 hours V1 1980
V2 1981
Uranus 2.871 billion km 50,724 km 84 years 17.9 hours V2 1986
Neptune 4.495 billion km 49,244 km 164.8 years 19.1 hours V2 1989
Home

Interactive Map

Purpose:

Add links with pop-up modals over a background image. The example uses a US map but you can use any background image.

This widget uses the same CSS and JavaScript as the Modals widget, in addition to its own CSS file.

To apply:

  1. Create a div with the class "mapContainer".
  2. Within mapContainer, insert an image with the class "mapBackground".
    • If your image is a png with transparent pixels, you can change its background color in the CSS.
  3. Also within mapContainer, add a div with the class "locationInstance" for each clickable element.
    • Your clickable elements can be plain text or images.
    • Add style="margin-top: -50%; margin-left: 50%;" within each locationInstance.
    • Change the margin-top and margin-left percentage for each locationInstance so it sits correctly above its segment. This will be trial and error. Margin-top should be negative.
    • Add onclick="openModal('myLocationID');" within each locationInstance, and update myLocationID to something appropriate to the clickable segment.
  4. Follow the instructions in the Modals widget to create the pop-ups.

Example:

California and New York are the only states with pop-up content for this example.

USA map.
AL
AK
AR
AZ
CA
CO
CT
DC
DE
FL
GA
HI
ID
IA
IL
IN
KS
KY
LA
MA
MD
ME
MI
MN
MS
MO
MT
NC
ND
NE
NH
NJ
NM
NV
NY
OH
OK
OR
PA
RI
SC
SD
TN
TX
UT
VA
VT
WA
WV
WI
WY

Original map image from Pixabay.

Home

Drop Menu

Purpose:

Create a drop menu for main navigation.

To apply:

  1. Create the button which opens the drop menu. It can go anywhere, but ideally should be in the upper right corner so the drop menu covers it when open. It should be placed in a div with the class "menuIconContainer" which calls the showDropMenu() function, like this:
    <div class="menuIconContainer" onclick="showDropMenu();">&#8801;</div>
  2. Create a page-wide div with a partially transparent gray background to serve as the drop menu's background when its open. It provides a surface that can be clicked to close the menu by clicking outside the menu. It can go anywhere in the HTML document because it has a fixed position. It should look like this:
    <div id="dropMenuBackground" onclick="closeMenuViaBackground(event);></div>
  3. Create a div for the drop menu itself. This also can go anywhere because it has a fixed position. Its opening tag should have the ID "dropMenu" and it should have an onclick call to the hideDropMenu() function, like this:
    <div id="dropMenu" onclick="hideDropMenu();">
  4. Within the dropMenu div, create the individual menu item divs. Each should have the class "menuItem".

Optional:

Example:

The first two options are regular links. The rest of the menu uses this widget's pagination option described above.

MAIN MENU
Home

Multiple Drop Menus

Purpose:

Create multiple drop menus within a navigation bar. The menus merge into one for narrow screens.

To apply:

  1. Create a page-wide div with a transparent background. It provides a surface that can be clicked to close the menu by clicking outside the menu. It can go anywhere in the HTML document because it has a fixed position. It should look like this:
    <div id="multiMenuBackground" onclick="closeDropMenuViaBackground(event);></div>
  2. Create the button which opens the drop menu on narrow screens. It should be placed in a div with the class "multiMenuIconContainer" and should call the showAllMenusStacked() function. Place this div within a parent div that has the style text-align: right;. It should look like this:
    <div style="text-align: right;">
      <div class="multiMenuIconContainer" onclick="showAllMenusStacked();">&#8801;</div>
    </div><!-- End right-align div. -->
  3. Create a div which serves as the horizontal menu bar that spans the page. It will contain the tabs which open the menus. It should have the id "menuBar" and should be placed right below the button in the step above.
  4. Within the menuBar div, create a div for each drop menu's tab. Each should have the class "menuBarTab".
  5. Within each menuBarTab, create a div to contain the tab's label text (and/or image). This div does not need a class or an ID. But it needs an onclick statement to open its associated drop menu. Pass the ID of its drop menu as a parameter of the onclick function. So its opening tag should look like this:
    <div onclick="showDropMenuInstance('exampleMenu1');"gt;
  6. Beneath the above div with the tab label , create the individual drop menu itself. It should have the class "dropMenuInstance" and a unique ID. It should also have an onclick call to the hideDropMenus() function. So the opening div should look like this:
    <div class="dropMenuInstance" onclick="hideDropMenus();" id="exampleMenu1">
  7. Within the dropMenuInstance div, create the individual menu options. Each should be in a div with the class "menuInstanceOption".

Optional:

Example:

The first drop menu uses this widget's pagination to change pages. The second drop menu has regular links. The rest of the drop menus have no function.

Normally a menu bar like this would be at the top of your page, so they would not overflow your page like these do. But this is just for demonstration.

Narrow your browser to see the menu bar and drop menus stacked for narrow screens.

Menu 1's page 1.
Menu 1's page 2.
Menu 1's page 3.
Menu 1's page 4.
Menu 1's page 5.
Menu 1's page 6A.
Menu 1's page 6B.
Home

Icon Menu

Purpose:

Create an icon menu, like an app menu on mobile devices, for your site's navigation.

To apply:

  1. Wrap each page in a div with the style "widgetPage" and a unique ID, like this:
    <div class="widgetPage" id="YourWidgetID">
  2. Add a navigation menu. Copy over the one from the example HTML file. You just need to change the icon, its alt tag, and the ID in the function call for each widgetPage. The navigation menu can be placed anywhere outside the widgetPages. (You can just use plain text instead of an icon.)
  3. To let users return to the homepage, create a "Home" or "Back" button which passes your homepage's ID in its function call. This should be placed on each widgetPage. You can also copy this from the example HTML.

Please note, the JavaScript for this widget must be placed after the related content. It can be embedded in a script tag or referenced externally. But it will not work if referenced above the content.

Example:

The page selections on this site's homepage use this widget.

But this site's version has a few additional features. To keep things simple, I have created a basic version above and the advanced version in use on this site, which is also available to you with instructions.

Use this JavaScript file instead.

In addition to the basic menu above, the advanced version also has these features:

  • Add a home icon (or text) which appears only if the homepage is not open, like this site has in the upper right corner. Wrap the intro icon in a div with the class "mainIcon". Wrap the home icon in a div with the class "homeIcon", and make add this function call:
    onclick="openWidgetPage('home');"
    Please note for this to work your homepage must have the ID "home", unless you want to change it in the openWidgetPage() function too.
  • If a widgetPage has video or audio which is playing, it will keep playing even if a user navigates away from the page. (Because the page is just hidden when another is revealed, but is still present.) This version of the code stops any running media when a new page is opened.
  • A widgetPage can be opened directly if a hash already exists on page load. That way you can share a page with a direct link. (But the page will jump down to the start of the widgetPage.) Because some of my widgetPages don't load their scripts until the page is opened via the icon menu, I have opted to always open to the homepage on first load. You can do it either way by removing one line and uncommenting another. There are comments in the code to find them.
  • Very important: users are likely to click the browser's back button rather than click the home icon. (I keep doing it myself.) Because these pages are all really one, with content hidden and revealed onclick, the browser's back button would take users offsite back to wherever they had been. Therefore the widgetPage's ID is added to the URL as a hash, which adds it to browser history. However, clicking back and forth through the history would not alter the content, so there is additional code to read the hash back in and open the corresponding page. Please note the following:
    • Your widgetPage IDs will be added to the hash in the URL, so they should make sense to users.
    • If you already have anchor links somewhere in your page, they will interfere with this function since the text after the hash is used to open a corresponding widgetPage.
    • Because the page had no hash on page load, an extra back click is required to back all the way out, even though home content is displayed. This is unavoidable, but not really much of a problem.

You can remove these features as you see fit if you feel comfortable with JavaScript without breaking the widget. There are plenty of comments in the code to help you.

Home

Filter by Keyword

Purpose:

Filter displayed items by keywords.

To apply:

  1. Add the class "keywordContent" to each div you would like displayed by selection.
  2. Add keywords to each div's class tag as if they were additional classes. For example:
    class="keywordContent keywordOne keywordTwo"
    (The keyword classes do not need to have any styles or be referenced by CSS at all.)
  3. Create the elements which should be clicked to filter the keywordContent divs. Each should have an onlick to the showKeywordContent() function, and pass each keyword as an argument, separated by a comma, like this:
    onclick="showKeywordContent('keywordOne', 'keywordTwo');"
  4. Optional: Create a "View All" button (which in effect acts like a reset) by creating an element which calls the viewAllKeywordContents() function like this: onclick="viewAllKeywordContents();"

Please note, the JavaScript for this widget must be placed after the related content. It can be embedded in a script tag or referenced externally. But it will not work if referenced above the content.

There is really no CSS associated with this widget, except to set the keywordContent divs to display as "none", "block", or "inline-block".

Variations:

  1. Hidden on Load: The code assumes you want all keywordContent divs to display on page load. But you can set their style to "display: none;" and let users load them via keywords. You will have to remove the first for-loop in the showKeywordContent() function because it hides all with each button click before revealing a selection. In this case, you might need a reset button which hides all the keywordContent divs. You can do so with this function call:
    onclick="hideAllKeywordContents();"
  2. Block or Inline-Block: The JavaScript, and the example below, assumes small items so the function resets their style from "none" to "inline-block". If your divs are page-wide, just reset the JavaScript style update to "block". You will have to do it in both functions.
  3. keywordContent Groups: You might have two or more groups of keywordContent divs which are meant to function independently of each other. To get the solution which makes the groups function independently, click here.
In addition to the steps above, do the following:
  1. Wrap each group of keywordContent divs in parent div, and apply a unique ID to the parent div's opening tag.
  2. Make the parent div's ID the first parameter in the showKeywordContent() function call, like this:
    onclick="showKeywordContent('parentDivOne', 'keywordOne', 'keywordTwo');"
  3. Reference this HTML instead.
  4. Use this JavaScript file instead.

Example:



A
B
1
2
Home

Reveal by Password

Purpose:

Reveal hidden content by password.

Caution: This is a very simple password check. Anyone who knows how to look at page source can find the password. It should not be used for sensitive or private information.

To apply:

  1. Update the password in the JavaScript code. It's at the start of the code.
  2. Update the message that the user sees if the password is incorrect, also in the JavaScript code.
  3. Create an input field with the ID "passwordField". This is where the user enters the password.
  4. Create a button, or any element, with the ID "passwordButton". This is the submit button.
  5. Place the locked content within one or more elements with the class "lockedContent" and apply the style "display: none;". Since this is the only style for this widget I did not create a separate style sheet for it.
  6. Optional: If you want the instructions to the user to disappear along with the input field and submit button, apply the ID "passwordInstructions" to the instructions element.
  7. Optional: By default the password and user input comparison is case sensitive. There are two lines in the JavaScript file which make the comparison case insensitive. They are commented out and noted in the code. Just remove the "//" comment tags at the start of the lines to make them active.

Example:

Please enter the password below to reveal content.
(The password is "widgets".)

Home

Display by URL

Purpose:

Filter displayed items by a term in the URL.

To apply:

You can add words to a URL without breaking the link by placing them after either a "#" or a "?". You can then hide, display, or alter content on load based on those words. That way users will see different content based on the URL you provide.

  1. Add a "#" or "?" to your URL and follow it with one or more words.
  2. Add an ID to any content that should be hidden or revealed.
    • If you have multilple adjoining elements, wrap them in a div and add an ID to that instead.
    • If you have multilple scattered elements, it might be better to apply a class instead. More on that below.
  3. Add style="display: none;" to any content meant to be hidden if someone accesses the page with the regular URL.
  4. Add this to the bottom of the page:
    <script>
    if(window.location.href.indexOf("xxx")){
      document.getElementById("yyy").style.display = "block";
    } // End if.
    </script>
    • XXX is the term in the URL that triggers the changes. Change it to your term.
    • YYY is the ID of the element to be revealed. Change it to your ID.
    • You can also change the display style to "inline-block" depending on your needs.
    • If you want to hide content, change the display style to "none".
    • You can add as many document.getElementByID... lines as you need.
    • If you know JavaScript, you can add any other changes to the page you like within the if statement.
  5. If you have multiple terms which trigger various page changes, just add other if statements.

There is no further CSS or JavaScript beyond the above for this widget.

Example:

This site uses a URL variations to trigger content change on load, so that the widgets can be accessed directly. For example, the two links below go to the same page, but the hashtag causes different content to display.

Home

Quiz

Purpose:

Have your users take a quiz which provides a score, and reveals some content if the user passes, like a link or some text.

When you click "Submit" the quiz will:


To apply:

Create a div with the class "quizContainer". All of the quiz's contents and options below must be placed within it.


To create a multiple choice (including true or false) question:

  1. Wrap the entire question/answers set in a div with the class "questionContainer".
  2. Wrap just the question itself in a div with the class "question".
  3. Wrap each answer in a div with the class "answer".
  4. For correct answers, add the class "correctAnswer" so it looks like class="answer correctAnswer".
  5. Add a name in each answer's input tag. The name should be the same for all answers within a question, but different from other questions, so something like name="question1Answer".
  6. Optional: If an answer has an explanation, place that in a div with the class "answerExplanation", and place that before the closing tag of the questionContainer div.

To create a "select all that apply" question:

  1. Follow steps 1-5 for the multiple choice question.
  2. For the input tag, change type="radio" to type="checkbox". These inputs do not need a name.

To reveal branching answers when an answer is selected:

  1. For the answers in the branch, follow the same rules for classes, correct answers, types, and names, as above. Wrap each set of branching answers in a div with the class "answerBranch", and place the div below the parent answer that opens it.
  2. Add a unique ID to any questionContainer which contains an answerBranch div.
  3. Add this function call to all answers outside the branch in the question, so the branch is hidden when another main answer is selected:
    onclick="hideAnswerBranch('questionContainerID');"
    (Replace "questionContainerID" with the ID of the questionContainer that contains the answerBranch.)
  4. Add a unique ID to each answerBranch div.
  5. For the parent answer which reveals the branch if clicked, add an additional call to the call to the revealBranch() function and pass your answerBranch ID as a parameter, so it looks like this:
    onclick="hideAnswerBranch('questionContainerID'), revealBranch('answerBranchID');"
    (Replace "questionContainerID" with the ID you applied to the questionContainer that contains the answerBranch. Replace "answerBranchID" with the ID you applied to the answerBranch.)

To apply the "Submit" button.


Optional post-submit divs.

  1. If you would like to reveal a message if the user passed, place it in a div with the class "quizPassedContent".
  2. If you would like to reveal a message if the user failed, place it in a div with the class "quizFailedContent".
  3. If you would like to reveal a score, add an empty div with the class "scoreContainer". It will be filled in with the score upon clicking submit.
    • The first statement in this widget's JavaScript is the variable "minNumberToPass". This represents the number of questions a user must get right to reveal either the "quizPassedContent" div or the "quizFailedContent" div. Update the number to suit your needs.

Example:


Quiz Template

1. The sky is blue.
A. True
B. False
Explanation: Find out why here.
2. How many days does a week have?
A. 7
B. 5
C. 6
D. 9
2. The grass is
A. Blue
B. yellow
C. Other
C1. Green
C2. purple
4. Which of these is used to make cider?
A. Apple
Apple.
B. Bananas
Bananas.
C. Pear
Pear.
D. Pineapple
Pineapple.
5. Which are months? Select all that apply.
A. November
B. Saturday
C. April
D. Cloud
E. Pacific
Thank you. You passed!
Sorry, you did not pass. Please try again.
Home

Change Media

Purpose:

Change video or audio with a click.

Please note, this only works if the media is contained on the same domain. For example, all on your domain, or all on YouTube, or all on Vimeo. That's because we are only replacing the media source reference, and due to a JavaScript security feature, we can't jump sources between domains. If you have media from different sources, just replace the entire block of code for the embedded media by using the Replace Contents widget instead.

To apply:

  1. Get the embed code of the video you would like to display on page load.
    • If it's a YouTube video, you will find it by clicking the "Share" icon, then selecting the entire iframe.
    • If it's just a video vile on your own site, use a video tag.
  2. Add the ID “currentVideo” in the iframe/video opening tag.
  3. Get the src for each of the videos you want to make available.
    • If it's a YouTube video, you will find it in the iframe.
    • If it's your own video, it's just the URL.
  4. Create an element to call each video option, and add this onclick statement to each:
    onclick="changeVideo('videoSrc', 'videoCaption');"
    • The first parameter in the function call is the Video’s src.
    • The second parameter (optional) is the text for the caption. If you don’t want an updateable caption, remove it along with the comma.
  5. Optional: Create the updateable caption by placing the text in a div with the ID “videoCaption”. The default text should be the first video’s caption (because the first video is present onload) but it doesn’t have to be. This can go anywhere.
  6. Optional but highly recommended: Make the video responsive by placing the entire iframe in a div with the class “videoContainer”.
    • If you're using a video or audio tag instead of an iframe, update the CSS selector .videoContainer iframe accordingly.

Example:

Depeche Mode: Where's the Revolution

SELECT A VIDEO

Home

Web Memory

Purpose:

Save visitor data and display different content for return visits.

To apply:

There are several ways to save data. We will look at cookies and sessionStorage objects. They differ in the following ways:

You can also use localStorage objects, which are very similar to sessionStorage objects, but they stay in memory indefinitely. I don't like adding permanent data to users' computers so we will just look at cookies and sessionStorage objects. You can get more information on both at w3schools' web storage page.

Select either cookies or sessionStorage objects below for an example of each.

A cookie is just data in a name-value pair. For example:
planet = Earth

You can create a cookie like this:
document.cookie = "planet = Earth";
Avoid special characters like /,\,&,'," and spaces. Alphanumeric characters only.

You can add a parameter which sets the cookie's expiration date. Otherwise the cookie will be deleted when the browser session ends (when the user closes the browser) by default. For our purposes the default should be enough. But if you want to know more about cookie duration please visit the W3 Schools' cookie tutorial.

It is relatively easy to check if a cookie exists, and then do something based on that alone. But it is quite complicated to retrieve the value of a cookie. If you want to retrieve data, I suggest using sessionStorage instead.

Example:

We will display a message on this page for returning visitors based on the existence of a cookie. By clicking the button below, you will create this cookie:
document.cookie = "returningVisitor = yes";

Each time this page loads, a function checks if the cookie exists. If it does, a welcome message will display instead of these instructions.

You can create the cookie automatically on page load without the button function, so that a returning user will see something different automatically. That version of the function is also in the cookies' JavaScript file, commented out.

If you created the cookie, refresh this page, and navigate back here. You will see the welcome message instead of these instructions.



A sessionStorage ojbect is data in a name-value pair. For example:
planet = Earth

You can create a sessionStorage object like this:
sessionStorage.setItem("planet", "Earth");
Avoid special characters like /,\,&,'," and white space. Alphanumeric characters only.

You can retrieve the value of a sessionStorage object and place it in a variable, or whatever else you want to do with it, like this:
var myPlanet = sessionStorage.getItem("planet");
The variable myPlanet will contain "Earth".

Example:

We will display a personalized message on this page for returning visitors which includes their first and last name. By entering your name and clicking the button below, you will save the data to sessionStorage objects.

Each time this page loads, a function will check if the sessionStorage object exists. If it does, a welcome message will display instead of these instructions.

Please enter your first name:



Please enter your last name:



If you submitted your name, refresh the page, and navigate back here. You will see your personalized message.

Home

Calculator

Purpose:

Display a calculator that can perform simple mathematical functions..

This is an admittedly useless widget because all phones and Google have a calculator. But it was a fun challenge to create. Parts of it can be useful to you. Look through the CSS or JS to see if there is anything you can apply elsewhere. For example, the click animation on the buttons.

To apply:

There are really no steps here beyond copying over the code and updating the CSS as you see fit.

Example:


7
8
9
+
4
5
6
-
1
2
3
0
.
Neg
/
C
❮ X
Exp
=
Home

Analog Clock

Purpose:

Display a functioning analog clock.

This is an admittedly useless widget if you don't need a clock. But it was a fun challenge to create. Parts of it can be useful to you. Look through the CSS or JS to see if there is anything you can apply elsewhere.

To apply:

There are really no steps here beyond copying over the code and updating the CSS as you see fit. There are some interdependencies in the styles, so check the comments within before changing anything.

You can remove the pendulum, but adjust the large bottom margin on the clockContainer if you do so.

Example:


1
2
3
4
5
6
7
8
9
10
11
12
Home

FAQ

Errors

When I click the buttons, nothing happens.

Some of these widgets require that the script reference be placed after the elements they affect. This is noted in the instructions and in the JavaScript comments for those widgets.

The styles seem to be breaking, and everything is displaying all at once, or not at all.

Did you embed the CSS into an HTML page? If so, remove the comments. Sometimes comments in a style tag create breaks when embedded in an HTML document.

Also, try adding type="text/css" to the opening style tag, and try adding type="text/javascript" to the opening script tag.

I applied your styles but my page is not responsive. It still looks full-width on narrow screens.

Mobile devices interpret pixels differently. To make them respond to the CSS concept of a pixel, put this code in your page's head tag:
<meta name="viewport" content="width=device-width, initial-scale=1.0">

On a narrow screen, I get a left/right scroller at the bottom. Why does my page not fit?

There can be several reasons:

I have video or audio embedded in my widget section. When the section closes I can hear them still playing. How do I make them stop when the section closes?

You're just hiding the section, not removing it, so the media is still there playing. But there is a way to make them stop with JavaScript.

  1. Add the class "myMedia" to all media within sections that can be closed.
  2. Add the JavaScript for the stopMedia() function where appropriate.
  3. Call the stopMedia() function when the section closes. There are a few ways to do that:

Add the call to the stopMedia() function to the button that closes the section. For example, if you're stopping a video when a modal closes, then your function calls looks like this:
onclick="closeModal('modalID'), stopMedia();"

Modals have a closing button. But what about widgets whose sections close only when another has opened? For example, choosing another widget on this site's homepage. There is no closing button involved.

Then it becomes a matter of placing the function and its call in the right place. You would have to add the stopMedia() function call to every instance of an opening button for the other pages.

But that's a bit messy. Instead, place the stopMedia() function call within the widget's section opener function. That way stopMedia() is called whichever section a user opens next, even if media was never played, which is still okay.

So if I have a video on one of this site's main widget pages, and I want to make sure the video stops playing when you return to the homepage, then I would place the stopMedia() function call before the end of the openWidgetPage() function in the JavaScript for the icon menu.

What stopMedia() actually does is force the media to reload its src, which stops it and takes it back to the start. If you have a lot of videos, this reload process can slow things down a bit, but there is no better solution.

Example:

Start the video below. The button which opens this solution has the stopMedia() function call. When you click it again to close the solution, the video will stop playing.

Coding Tools & Tips

Is there a good, free WYSIWYG that generates HTML while you edit it as if it were a Word file?

There really is not. They all generate junky code which will leave you troubleshooting for mysterious margins, spaces, characters, etc. And they're likely to wreck code throughout your page due to one wrong entry. Even the paid ones like Dreamweaver have issues.

However I would use free online editors for simple things. For example, if you're copying something out of a Word file, and you just need to create paragraphs, numbered or bulleted lists, italics and bolds, etc. I would not attempt to apply responsive images or complex styles through these editors.

For simple things this page is quite useful. You can edit content as if it were a Word file, and view your work in both graphic and code layout.

Is there a good, free code editor?

I personally like to start with a blank HTML page and build it up with direct hard coding. It's easier than it sounds because once you get it right, the rest is a lot of copy/alter. There are fewer surprises and no extraneous code that way.

Notepad++ is all you need to create simple to moderate websites, especially if you become familiar with some of its find/replace with regular expression features. (Please note, this is not regular Notepad that comes with Windows.) This also has very helpful, but not intrusive, means of alerting you when code is broken, like simple color coding for example. You can download Notepad++ here.

Notepad++ does not work on Macs, but Mac users can install Sublime Text which is very similar.

If you feel like you need something more complicated, and rather headache inducing, you can download either Netbeans or Eclipse. These are enterprise-level code editors used to create everything from simple sites to complicated apps. They create a lot of interconnectivity between files for you, like HTML-to-CSS-to-JS, so that changes to file locations or names will auto-update for the whole project. They're also very good at pointing out errors. However just starting a project is a messy process. It's also ridiculously easy to break the interconnectivity between files, and once that happens, give it up and start over! Just opening the project on another computer is enough to break the connectivity. If you're good enough to use these to code apps, you're probably too advanced for my site.

Where is the best, free site to get answers to HTML, CSS, or JavaScript questions?

The World Wide Web Consortium, which is the official caretaker of HTML and other web-related issues, maintains an excellent site for tutorials and examples of all of the above and more - W3Schools.

You can also try Code Academy. It's not bad, but I find after I complete a course that the content was slim, too specific to the project underway, and just tedious.

Of course the best thing to do with a coding question is just google it. You'll find someone else has already asked the question, and you will find several message boards where someone else has (probably condescendingly) answered it. Most google searches will probably lead you to W3Schools anyway.

(By the way, I've had employers tell me the answer they want for "What do you do if you have a coding problem?" is "just google it". They want to know you can find the answer when you need it.)

Something is wrong on my site but I can't determine where. Is there a free service that will quickly check my HTML files for errors?

Yes. W3Schools has an excellent and quick code validator. You can either link to a page or upload a file to it. Don't be alarmed if it tells you that you have 100 errors. If you start from the first and work your way down, each fix will simultaneously correct many errors below.

Is there something like the above for CSS files?

Yes. W3Schools also has jigsaw, a CSS validator, which is almost exactly like the HTML validator.

Is there something like the above for JavaScript files?

No, unfortunately. Since the logical route to effective JavaScript can vary greatly between programmers and context, there is no way for a validator to determine what the script is meant to do. However, a good code editor will point out where you have a syntax problem, although they can't do anything if there's a logic problem.

Did you create all those animations?

No, they're built into CSS. I just altered them as I needed to. You can learn all about them at W3Schools. They even have a downloadable CSS page with all animations, which I have made available here. As you can see the page is quite a compacted jumble and difficult to interpret and manipulate without going through their tutorials, but you can have them all in one go and just pull out and clean up the ones you need.

Would you recommend one web-hosting service over another?

There are many options, and many more sites debating which option is best. I have only used GoDaddy, which is probably the most popular service. Their user interface can be difficult to understand at first. But from what I gather their servers are least likely to slow your site down and can handle a lot of traffic. I would shop around and read reviews.

Is there a site to get free images?

There are a great number of them, whether you're looking for images or icons. But some require emails, or memberships, or have limits unless you pay. I find most of what I need from pixabay. That's where the fruit images came from. I made the menu icons with PowerPoint and Photoshop. I made the background image with Photoshop.

Is there a good, free image editor?

Yes, Gimp2 is quite good. It's a lot like Photoshop, although not as broad or precise. But you get a lot for a free, open-source app. If you already know Photoshop, it's just a matter of learning a new, and somewhat messy, interface. If you don't know Photoshop, there's a learning curve to both anyway. It seems to take a lot of active memory though, but that shouldn't be a problem with a decent computer.

Is there a good, free FTP app?

There are a few, and the one I've used most is filezilla. It's probably the most popular too. It's quite good, easy to use, and free. It doesn't have robust security, so I wouldn't access a server through it on public wi-fi.

What is considered good website layout?

Clean and simple, columnar layout without borders, white space and plain backgrounds. Use images sparingly. Basically look at the index page of a news magazine, and make it look like that.

So why didn't I follow the above advice? I was trying to make something that looked more like my phone's home screen. And I just liked the background.

Tabs or spaces in coding?

Fans of the hilarious HBO show Silicon Valley might remember an argument between Richard and his girlfriend over using tabs versus spaces in coding. I'm with Richard - tabs! They look much better in a code editor. However, tabs are not always consistent across devices, and as you can see if you open my code links in a browser, they make code look very messy outside a code editor. But I set these up for ease in a code editor, and not for appearance. If you would rather convert the tabs to spaces, click below.

We're going to replace each tab with two spaces. Don't replace tabs with only one space so that you can reverse it easily if necessary. This works in Notepad++, but you should be able to do something very similar in any code editor.

  1. In Notepad++, bring up the Search feature with CTRL+F.
  2. Click "Wrap around". This will search throughout the document.
  3. Click "Regular Expression". This looks for special symbols.
  4. Click "Replace" in the top menu.
  5. In the "Find what" field, enter "\t" (without the quotes). This is the symbol for tabs.
  6. In the "Replace with" field, enter two spaces.
  7. Click "Replace All".

About This Site

What's the point of this site?

I have used popular code-libraries like JQuery and Bootstrap. I found trying to change styles frustrating because there is so much code in each widget, and it's often not clear what you're changing. The JavaScript is so compressed it's difficult to disentangle for changes. I wanted to make something simple and modifiable that can take any content.

What's with the weird Latinesque text throughout?

That is ipsem lorem, traditional placeholder text when you just need some content.

What's with all the fruit?

This site is in dire need of some color, and the images were free from pixabay. And I just like fruit.

Should you be credited?

If you find any of my content useful, it would be great if you would link back to this site.

Contact

My question wasn't answered. How do I contact you?

You can contact me at alternativewidgets@gmail.com. Please allow a few days for response.

Home

Updates

April 2022

March 2022

January 2022

February 2021

December 2020

September 2020

July 2020

June 2020

May 2020

April 2020

December 2019

October 2019

September 2019

August 2019

June 2019

May 2019

December 2018

September 2018

August 2018

Home