In the process of creating the Netmatters website facsimile the page's client section offered 2 challenges: The image has to change colour when hovered over, and the tooltip describing the client has to appear above the client's image when the image is being hovered over.
The solution that I used for the first challenge was using 2 images in the same div, where one of them is not displayed depending on whether or not the image's container is hovered over. See comments 1-3 in the code for more details.
The solution for the second challenge was solved by setting the position of the discription's container and arrow to absolute so it can be placed outside the flow of the page, extra width is added so the description can be fully displayed and the box has the correct dimensions and the "arrow" under the box is placed correctly by using transform: translateX to center it under the description. See code comments 4-8 for more details.
<!-- HTML ------------------------------------------------------------------------------------------------------------------>
<div class="client">
<div class="client-descr-box">
<div class="client-descr">
<h3>Busseys</h3>
<p>One of the UK's leading Ford dealerships.</p>
<div class="arrow"></div>
</div>
</div>
<a class="img-link" href="#">
<img src="https://www.netmatters.co.uk/uploads/page/1/home-T5gi.jpg" alt="">
<img src="https://www.netmatters.co.uk/uploads/page/1/home-gZQR.png" alt="">
</a>
</div>
<!-- CSS ------------------------------------------------------------------------------------------------------------------>
<!-- 1. Makes it so that the second image (the coloured version of the logo) is not displayed -->
.img-link img:nth-child(2) {
display: none;
}
<!-- 2. Makes it so that the second image (the coloured version of the logo) is displayed on :hover -->
.img-link:hover img:nth-child(2) {
display: block;
}
<!-- 3. Makes it so that the first image (the grey version of the logo) is not displayed on :hover -->
.img-link:hover img:nth-child(1) {
display: none;
}
<!-- 4. Displays the client's description box when the box' parent div is hovered over -->
.client:hover > .client-descr-box {
display: block;
}
<!-- 5. Sets the description box to not show by default,
its position to absolute so it can be made to appear above the actual client image,
adds width so the description box has the right dimensions,
and a margin at the bottom so there's room for the "arrow" at the bottom does not overlap the client image -->
.client-descr-box {
display: none;
bottom: 100%;
left: -100px;
right: -100px;
position: absolute;
margin-bottom: 50px;
}
<!-- 6. Sets the background, text alignment and colour,
padding, margin and width for the client description -->
.client-descr {
display: block;
background: colours.$dark-grey;
text-align: center;
color: white;
padding: 20px;
max-width: 260px;
margin: 0 auto;
}
<!-- 7. Sets the positon for the "arrow" that points to the client's image -->
.arrow {
top: 100%;
}
<!-- 8. Sets the shape of the "arrow" element,
the border-width dictates the shape of the arrow with 35px being the width of the top and 36px the sides of the triangle,
its positon to absolute so it can be placed correctly outside the client image,
and the transform property makes it so the arrow is centered above its client image -->
.arrow:after {
content: "";
display: inline-block;
border-width: 35px 36px 0;
border-color: #333645 transparent transparent;
border-style: solid;
position: absolute;
left: 50%;
top: 100%;
transform: translateX(-50%);
}
One of the tasks, that were part of my JavaScript learning process, was to add a typewriter effect to my portfolio's hero image.
I decided that in order to help become more proficient in JavaScript, I wanted to write my own typewriter script instead of using a pre-made plugin or code snippet.
After studying examples and analysing how exactly they work I decided on the above approach. The code is commented for a more detailed explanation of how it works.
// Declares the text to write
const text = [
" I'm a Web Developer",
" I'm a Software Developer",
" I'm a Game Developer",
" I'm a Designer"
];
// Declares counters for the typing effect, what sentence gets typed and set erasing to false, causing the script to type by default
let i = 0;
let textIndex = 0;
erasing = false;
function typeWriter(){
// Default state, erasing = false implicitly means typing = true
if (erasing === false){
// Checks if value of the counter i is lower the sentence's length, and if it is it will:
if (i < text[textIndex].length) {
// Adds letters to the element, where:
// The i in charAt(i) stands for the number of the letter in the sentence
// The i is increased by 1 so the script will type out the next letter
// The script then waits 100ms before running again
document.querySelector(".typewriterItem").textContent += text[textIndex].charAt(i);
i++;
setTimeout(typeWriter, 100);
}
// If the counter is not lower than the sentence, it will:
else {
// Set the erasing state to true, which will trigger the script to start deleting
// Before proceeding, the script waits for 1 second
erasing = true;
setTimeout(typeWriter, 1000);
}
}
// Erasing state
else if (erasing = true){
// Checks if value of the counter i is is not 0, and if it is not it will:
if (i !== 0) {
// Removes letters from the element, where:
// The i in slice(0, i) stands for the number of the last letter in the sentence
// The i is decreased by 1 so the script will remove the next letter
// The script then waits 100ms before running again
document.querySelector(".typewriterItem").textContent = text[textIndex].slice(0, i);
i--;
setTimeout(typeWriter, 75);
}
// If the counter is 0, depending on the textIndex counter that tells what sentence to type out, it will:
else {
// Ïf the text array counter is 3 (the last sentence in the array) it will:
if (textIndex === 3) {
// Sets erasing to false, which will cause the script to start typing again
// Waits 0.1 second
// Sets the textIndex counter to 0, which will make the script type out the first sentence again
erasing = false;
setTimeout(typeWriter, 100);
textIndex = 0;
}
// Ïf the text array counter is not 3 (the last sentence in the array) it will:
else {
// Sets erasing to false, which will cause the script to start typing again
// Waits 0.1 second
// Adds 1 to the textIndex counter, which will make the script type out the next sentence
erasing = false;
setTimeout(typeWriter, 100);
textIndex++;
}
}
}
}
// Runs the script when it's loaded in
typeWriter();
In order to learn SQL I had to complete the SQL Database Challenge where I had to write a query that includes a sub query with formatted outputs that are sorted by a value.
We were given the choice of 3 databases and I picked a movie database that holds data on movies, their cast, director, genre, ratings and reviewers.
I used the following tables
The above image shows the structure of the database used for this project, and all the relations between the different datasets.
mov_id | mov_title | mov_year | mov_time | mov_lang | mov_dt_rel | mov_rel_country |
---|---|---|---|---|---|---|
901 | Vertigo | 1958 | 128 | English | 1958-08-24 | UK |
902 | The Innocents | 1961 | 100 | English | 1962-02-19 | SW |
903 | Lawrence of Arabia | 1962 | 216 | English | 1962-12-11 | UK |
904 | The Deer Hunter | 1978 | 183 | English | 1979-03-08 | UK |
905 | Amadeus | 1984 | 160 | English | 1985-01-07 | UK |
906 | Blade Runner | 1982 | 117 | English | 1982-09-09 | UK |
907 | Eyes Wide Shut | 1999 | 159 | English | UK | |
908 | The Usual Suspects | 1995 | 106 | English | 1995-08-25 | UK |
909 | Chinatown | 1974 | 130 | English | 1974-08-09 | UK |
910 | Boogie Nights | 1997 | 155 | English | 1998-02-16 | UK |
911 | Annie Hall | 1977 | 93 | English | 1977-04-20 | USA |
912 | Princess Mononoke | 1997 | 134 | Japanese | 2001-10-19 | UK |
913 | The Shawshank Redemption | 1994 | 142 | English | 1995-02-17 | UK |
914 | American Beauty | 1999 | 122 | English | UK | |
915 | Titanic | 1997 | 194 | English | 1998-01-23 | UK |
916 | Good Will Hunting | 1997 | 126 | English | 1998-06-03 | UK |
917 | Deliverance | 1972 | 109 | English | 1982-10-05 | UK |
918 | Trainspotting | 1996 | 94 | English | 1996-02-23 | UK |
919 | The Prestige | 2006 | 130 | English | 2006-11-10 | UK |
920 | Donnie Darko | 2001 | 113 | English | UK | |
921 | Slumdog Millionaire | 2008 | 120 | English | 2009-01-09 | UK |
922 | Aliens | 1986 | 137 | English | 1986-08-29 | UK |
923 | Beyond the Sea | 2004 | 118 | English | 2004-11-26 | UK |
924 | Avatar | 2009 | 162 | English | 2009-12-17 | UK |
926 | Seven Samurai | 1954 | 207 | Japanese | 1954-04-26 | JP |
927 | Spirited Away | 2001 | 125 | Japanese | 2003-09-12 | UK |
928 | Back to the Future | 1985 | 116 | English | 1985-12-04 | UK |
925 | Braveheart | 1995 | 178 | English | 1995-09-08 | UK |
The above table contains the main movie information, and connects directly or indirectly to all other tables by the mov_id key, where the other tables show detailed information.
dir_id | dir_fname | dir_lname |
---|---|---|
201 | Alfred | Hitchcock |
202 | Jack | Clayton |
203 | David | Lean |
204 | Michael | Cimino |
205 | Milos | Forman |
206 | Ridley | Scott |
207 | Stanley | Kubrick |
208 | Bryan | Singer |
209 | Roman | Polanski |
210 | Paul | Thomas Anderson |
211 | Woody | Allen |
212 | Hayao | Miyazaki |
213 | Frank | Darabont |
214 | Sam | Mendes |
215 | James | Cameron |
216 | Gus | Van Sant |
217 | John | Boorman |
218 | Danny | Boyle |
219 | Christopher | Nolan |
220 | Richard | Kelly |
221 | Kevin | Spacey |
222 | Andrei | Tarkovsky |
223 | Peter | Jackson |
dir_id | mov_id |
---|---|
201 | 901 |
202 | 902 |
203 | 903 |
204 | 904 |
205 | 905 |
206 | 906 |
207 | 907 |
208 | 908 |
209 | 909 |
210 | 910 |
211 | 911 |
212 | 912 |
213 | 913 |
214 | 914 |
215 | 915 |
216 | 916 |
217 | 917 |
218 | 918 |
219 | 919 |
220 | 920 |
218 | 921 |
215 | 922 |
221 | 923 |
The Director Details table connects to the Director Table by dir_id, and the Director Table then connects to the master table by mov_id, the query joins them to the master table in the order they're shown.
mov_id | gen_id |
---|---|
922 | 1001 |
917 | 1002 |
903 | 1002 |
912 | 1003 |
911 | 1005 |
908 | 1006 |
913 | 1006 |
926 | 1007 |
928 | 1007 |
918 | 1007 |
921 | 1007 |
902 | 1008 |
923 | 1009 |
907 | 1010 |
927 | 1010 |
901 | 1010 |
914 | 1011 |
906 | 1012 |
904 | 1013 |
gen_id | gen_title |
---|---|
1001 | Action |
1002 | Adventure |
1003 | Animation |
1004 | Biography |
1005 | Comedy |
1006 | Crime |
1007 | Drama |
1008 | Horror |
1009 | Music |
1010 | Mystery |
1011 | Romance |
1012 | Thriller |
1013 | War |
The Genre table contains genre IDs and connects to the master table through mov_id, the Genre Details table contains the genre titles, connected to the Genre table by gen_id. The query joins them to the master table in the order shown.
mov_id | rev_id | rev_stars | num_o_ratings |
---|---|---|---|
901 | 9001 | 8.40 | 263575 |
902 | 9002 | 7.90 | 20207 |
903 | 9003 | 8.30 | 202778 |
906 | 9005 | 8.20 | 484746 |
924 | 9006 | 7.30 | |
908 | 9007 | 8.60 | 779489 |
909 | 9008 | 227235 | |
910 | 9009 | 3.00 | 195961 |
911 | 9010 | 8.10 | 203875 |
912 | 9011 | 8.40 | |
914 | 9013 | 7.00 | 862618 |
915 | 9001 | 7.70 | 830095 |
916 | 9014 | 4.00 | 642132 |
925 | 9015 | 7.70 | 81328 |
918 | 9016 | 580301 | |
920 | 9017 | 8.10 | 609451 |
921 | 9018 | 8.00 | 667758 |
922 | 9019 | 8.40 | 511613 |
923 | 9020 | 6.70 | 13091 |
The Ratings table contains data on ratings and reviews, it connects to the Master table through mov_id. The query places it at the very end of the results table.
<!-- Selects the following:
movie title and year from the Master table,
director first and last name, from the Director Details obtained through the Directors table and concatenated to display as a single value,
genre title from the Genre Details table, obtained through the Genre table, and
rating from the Movie Ratings table-->
SELECT mov_title AS "Movie", mov_year AS "Year",
CONCAT(director.dir_fname, director.dir_lname) AS "Director",
genres.gen_title AS "Genre",
rating.rev_stars AS "Stars (Rating)"
FROM movie
<!-- Joins the tables in the following configuration:
director - movie_direction - movie - movie_genres - genres - rating -->
LEFT OUTER movie_direction ON movie_direction.mov_id = movie.mov_id
LEFT OUTER director ON director.dir_id = movie_direction.dir_id
LEFT OUTER movie_genres ON movie_genres.mov_id = movie.mov_id
LEFT OUTER genres ON genres.gen_id = movie_genres.gen_id
LEFT OUTER rating ON rating.mov_id = movie.mov_id
<!-- Filters the results to only display results where the movie's language is English,
a subquery that filters for the movie's year of release being before 1990 -->
WHERE movie.mov_lang = 'English'
AND movie.mov_year IN (SELECT mov_year FROM movie WHERE mov_year < 1990)
<!-- Orders the results by movie title in an ascending order -->
ORDER BY movie.mov_title ASC;
This SQL query joins the Director tables, Master table, Genre tables and Rating tables together. It then displays all movies where the language is English, were released before 1990 and are alphabetically ordered by title.
The results are displayed below:
Movie | Year | Director | Genre | Stars (Rating) |
---|---|---|---|---|
Aliens | 1986 | James Cameron | Action | 8.40 |
Amadeus | 1984 | Milos Forman | ||
Annie Hall | 1977 | Woody Allen | Comedy | 8.10 |
Back to the Future | 1985 | Drama | ||
Blade Runner | 1982 | Ridley Scott | Thriller | 8.20 |
Chinatown | 1974 | Roman Polanski | ||
Deliverance | 1972 | John Boorman | Adventure | |
Lawrence of Arabia | 1962 | David Lean | Adventure | 8.30 |
The Deer Hunter | 1978 | Michael Cimino | War | |
The Innocents | 1961 | Jack Clayton | Horror | 7.90 |
Vertigo | 1958 | Alfred Hitchcock | Mystery | 8.40 |