Jump to content

PHP beginner


fluffy82
Go to solution Solved by kicken,

Recommended Posts

Hi all,

I'm trying to write a page for my genealogy website, which eventually will show a French Republican calendar, a Gregorian calendar, and highlight corresponding dates between them when selected. My PHP knowledge is limited, but I'm trying to learn as I go. Some things really don't seem clear to me. Which is why I'm asking you for any hints or tips.

First step is trying to get the calendar to show as I want it to. I've included three months with 5 days each for testnig purposes.

The months show up correctly, the days are "calculated" correctly, but the display won't toggle when clicking on a month.
The .days div is standard "display: none", and when I select a month, the corresponding .days div should be changed into "display: block". But it doesn't. Clicking a month has no result.

Any ideas why? I'm quite sure there's something wrong with the <script> but I can't figure out what...

<!DOCTYPE html>
<html>
<head>
	<title>French Republican Calendar</title>
	<meta charset="UTF-8">
	<style>
  		.month-container {
    		margin: 10px;
  		}
  		.month {
    		display: inline-block;
    		margin: 10px;
   		 	padding: 10px;
   			background-color: #EEE;
    		cursor: pointer;
  		}
  		.days {
   		 	display: none;
    		margin-top: 10px;
   			padding: 10px;
    		background-color: #DDD;
  		}
  		.day {
    		margin: 5px 0;
    		font-weight: bold;
  		}
  		.name {
    		margin-left: 10px;
    		font-style: italic;
  		}
	</style>
</head>
<body>
	<h1>French Republican Calendar</h1>
	<?php
		$months = array(
			"Vendémiaire" => array(
				array("1", "Raisin"),
				array("2", "Safran"),
				array("3", "Châtaigne"),
				array("4", "Colchique"),
				array("5", "Cheval")
			),
			"Brumaire" => array(
				array("1", "Pomme"),
				array("2", "Céleri"),
				array("3", "Poire"),
				array("4", "Betterave"),
				array("5", "Oie")
			),
			"Frimaire" => array(
				array("1", "Raiponce"),
				array("2", "Turneps"),
				array("3", "Chicorée"),
				array("4", "Nèfle"),
				array("5", "Cochon")
			),
			);
		

foreach ($months as $month => $days) {
  echo "<div class='month-container'>";
  echo "<div class='days' id='" . htmlentities($month, ENT_QUOTES, "UTF-8") . "'>";
  foreach ($days as $day) {
    echo "<div class='day'>" . $day[0] . "<span class='name'>" . $day[1] . "</span></div>";
  }
  echo "</div>";
  echo "<div class='month' onclick='toggleDays(this)'>$month</div>";
  echo "</div>";
}
	?>

<script>
function toggleDays(el) {
  var days = null;
  var sibling = el.nextElementSibling;
  while (sibling) {
    if (sibling.classList.contains("days")) {
      days = sibling;
      break;
    }
    sibling = sibling.nextElementSibling;
  }
  if (days.style.display === "none") {
    days.style.display = "block";
  } else {
    days.style.display = "none";
  }
}
	var months = document.querySelectorAll(".month");
		months.forEach(function(month) {
  	month.addEventListener("click", function() {
    	toggleDays(this);
  });
});
</script>

</body>
</html>

 

Link to comment
Share on other sites

In case you're not aware, the console can also be used for debugging purposes. For example, you could add console.log() in different parts of your script to see if things are executing as expected. More information can be found here:
https://developer.mozilla.org/en-US/docs/Web/API/Console/log

You can also watch your "days" elements to see if they're changing to "block" or "none", as intended.

  • Thanks 1
Link to comment
Share on other sites

18 minutes ago, cyberRobot said:

In case you're not aware, the console can also be used for debugging purposes. For example, you could add console.log() in different parts of your script to see if things are executing as expected. More information can be found here:
https://developer.mozilla.org/en-US/docs/Web/API/Console/log

You can also watch your "days" elements to see if they're changing to "block" or "none", as intended.

 

Great advice.  And there is also a built in debugger, where you can set breakpoints in your javascript code, and step through line by line to see how variables change (or not).  You use the sources tab for that.

Link to comment
Share on other sites

  • Solution

The main issue with your code is how you're trying to find the days element.  To explain a bit, lets look at how your HTML gets rendered:

<div class='month-container'>
    <div class='days' id='Vendémiaire'>
        <div class='day'>1<span class='name'>Raisin</span></div>
        <div class='day'>2<span class='name'>Safran</span></div>
        <div class='day'>3<span class='name'>Châtaigne</span></div>
        <div class='day'>4<span class='name'>Colchique</span></div>
        <div class='day'>5<span class='name'>Cheval</span></div>
    </div>
    <div class='month'>Vendémiaire</div>
</div>

You are attaching your click handler to the div.month element.  That means in your click handler, el is going to reference that element.

To try and find the days element, you are looping over the nextElementSibling property until there are no more siblings.  Think about that for a moment.  nextElementSibling is going to find the elements that come after the current element.  What comes after div.next?  Nothing.  You need to be looking at the previous siblings, using prevElementSibling.

Now, once you find your days element, you have another problem.  You are testing if it's style.display value is equal to "none".  One might think this should be true since you have applied display: none; in your css, but it's not.  The reason is that the style properties on the element only refer to styles applied directly to that element via a style="" attribute, not anything inherited via css selectors.  Since no direct style was applied to your element, the style.display will be the empty string, fail your condition, and you'll set it to display.  On a second click, it would pass and you'd set block and your toggle would start working.  Reversing the conditional would fix this double-click issue.

---

Now that the issues have been pointed out, there are other things of note and also general ideas that should be applied to make this better.

  1. You have both an onclick="" attribute and an addEventListener call on your month elements.  You should only have one, and the preferred method is to use addEventListener in your code and stop using the on* attributes.  It should be removed, notice I didn't include it in my example html above.
  2. Your loop to find the days element is unnecessary.  The way your HTML is structured, the days element will be simply el.prevElementSibling, no looping required.  If you want to add other elements between your month div and the days elements, a better approach would be to use querySelector on the parent element: el.parentElement.querySelector('.days');
  3. Dealing with style properties directly should generally be avoided if possible.  Changing the applied classes is a better approach.  For something like this, create another class that sets display to block which can be applied to your days element.  You can then use javascript to toggle this class on and off.
        .days.visible {
            display: block;
        }
    days.classList.toggle('visible');

 

You can view the above suggestions implemented in this demo fiddle.

 

Edited by kicken
  • Like 1
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.