Related
I have a function that turns an array into modal window links as a template literal.
The code that creates the links works fine outside of the function
But once it gets rendered in the function it no longer works. I can't find any errors, but it does NOT work.
However, if I copy the HTML that the function renders and save that as actual HTML, that works fine on its own.
A good chunk of the JavaScript portion of the code is posted below. A full version is on Codepen.
There are two sections in the example on Codepen:
The first section has the code as it's rendered by the function.
The second section is copied from the Elements tab in Developer Tools and saved as actual HTML.
"use strict";
const modalBtns = document.querySelectorAll(".modal-button");
const modalWin = document.querySelector(".modal-window");
const closeBtn = document.querySelector(".close-modal");
const modal_iframe = document.getElementById("modal_iframe");
modalBtns.forEach((item) => {
item.addEventListener("click", function (e) {
let modal = e.currentTarget;
if (modal.dataset.target) {
let modalID = modal.dataset.target;
document.getElementById(modalID).style.display = "block";
}
if (modal.dataset.iframe) {
modal_iframe.src = modal.dataset.iframe;
document
.querySelector(".button-footer")
.addEventListener("click", function () {
window.open(modal.dataset.iframe, "_blank");
});
}
if (modal.dataset.header) {
document.querySelector(
".modal-header"
).innerHTML = `<h1>${modal.dataset.header}</h1>`;
}
if (modal.dataset.dimensions) {
document
.querySelector(".modal-window")
.setAttribute("style", modal.dataset.dimensions);
}
function loadIframe() {
let frame = document.getElementById("modal_window");
frame.style.height =
frame.contentWindow.document.body.scrollHeight + "px";
}
if (document.querySelector("#modal_window")) {
setTimeout(function () {
loadIframe;
}, 2000);
}
if (modal.dataset.reload && modal.dataset.reload === "1") {
document
.querySelector(".close-modal")
.addEventListener("click", function (e) {
console.log("parent.location.reload() pending...");
parent.location.reload();
});
}
/*======= All EventListeners Below Close Modal ================*/
closeBtn.addEventListener("click", function (e) {
document.querySelector(".modal-background").style.display = "none";
});
window.addEventListener("click", function (e) {
if (e.currentTarget === document.querySelector(".modal-background")) {
document.querySelector(".modal-background").style.display = "none";
}
});
document.body.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
document.querySelector(".modal-background").style.display = "none";
}
});
});
});
const main = document.querySelector("main");
const modal_links = [
{
link: "https://notation.netcentrx.net/staff/",
header: "Musical Staff",
thb: "notation",
w_h: "min-width:60vw;max-width:600px;height:650px",
reload: 0
},
{
link: "https://wsl.netcentrx.net/",
header: "WSL Commands",
thb: "wsl",
w_h: "min-width:60vw;max-width:600px;height:650px",
reload: 0
}
];
let modalLink = "";
function createModalLinks(
link,
modalID,
header,
img,
w_h = "width:90vw;height:600px",
reload = "0"
) {
modalLink = `
<a href="javascript:void(0)" class="modal-button" onclick="console.log('onclick handler:${link}');" data-header="${header}" data-target="${modalID}" data-iframe="${link}" data-dimensions="${w_h};margin-top:20px" data-reload="${reload}">
<img src="https://resume.netcentrx.net/examples/${img}.jpg" title="${img}" width="50">
</a>
`;
return modalLink;
}
let theLinks = "";
modal_links.forEach((item) => {
theLinks += createModalLinks(
item.link,
"modal_window",
item.header,
item.thb,
item.w_h,
item.reload
);
});
main.innerHTML = theLinks;
My apologies in advance for it not being stripped down to just the bare minimum. But in order to replicate the problem, it required more code than it probably should have had. I've been reworking this for the better part of a day without any insight as to what the real problem is. I've been creating functions using template literals just like this for years now, usually with a high success rate. Whatever the problem is, I need to know so I can get past it. The only anomaly that I spotted is that–in the version on Codepen–the only thing that doesn't work in that version is once the modal is displayed clicking on the background does not dismiss the modal like it does elsewhere. If that's significant as to what the problem may be, I'm not sure what the connection is.
Usually when I take the time to painstakingly write everything out like this I typically either spot the problem or figure out an alternative solution so there's no need to actually post a question, but this does not appear to be one of those times. As always, your help is very much appreciated!
The issue appears to just be timing. Your code is executed in order, and the first part gets all of the modal buttons on the page and sets the appropriate event listeners. Then the second part of your code adds 2 modal buttons, which were not present earlier.
By simply wrapping the first part of your code in a function and calling it later (or swapping the order of those two parts of code), everything works as expected.
"use strict";
const _InitModal = () => {
const modalBtns = document.querySelectorAll(".modal-button");
const modalWin = document.querySelector(".modal-window");
const closeBtn = document.querySelector(".close-modal");
const modal_iframe = document.getElementById("modal_iframe");
modalBtns.forEach((item) => {
item.addEventListener("click", function (e) {
console.log("e.currentTarget = " + e.currentTarget);
let modal = e.currentTarget;
console.log("modal = " + modal);
if (modal.dataset.target) {
let modalID = modal.dataset.target;
console.log("modal.dataset.target = " + modal.dataset.target);
document.getElementById(modalID).style.display = "block";
}
if (modal.dataset.iframe) {
modal_iframe.src = modal.dataset.iframe;
document
.querySelector(".button-footer")
.addEventListener("click", function () {
window.open(modal.dataset.iframe, "_blank");
});
}
if (modal.dataset.header) {
document.querySelector(
".modal-header"
).innerHTML = `<h1>${modal.dataset.header}</h1>`;
console.log(`modal.dataset.header = ${modal.dataset.header}`);
}
if (modal.dataset.dimensions) {
document
.querySelector(".modal-window")
.setAttribute("style", modal.dataset.dimensions);
}
function loadIframe() {
let frame = document.getElementById("modal_window");
frame.style.height =
frame.contentWindow.document.body.scrollHeight + "px";
}
if (document.querySelector("#modal_window")) {
setTimeout(function () {
loadIframe;
}, 2000);
}
// e.preventDefault();
if (modal.dataset.reload && modal.dataset.reload === "1") {
document
.querySelector(".close-modal")
.addEventListener("click", function (e) {
console.log("parent.location.reload() pending...");
parent.location.reload();
});
}
/*======= All EventListeners Below Close Modal ================*/
closeBtn.addEventListener("click", function (e) {
document.querySelector(".modal-background").style.display = "none";
});
window.addEventListener("click", function (e) {
console.log("e.currentTarget = " + e.currentTarget);
if (e.currentTarget === document.querySelector(".modal-background")) {
document.querySelector(".modal-background").style.display = "none";
}
});
document.body.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
console.log("e=" + e);
document.querySelector(".modal-background").style.display = "none";
}
});
});
});
}
const main = document.querySelector("main");
const modal_links = [
{
link: "https://notation.netcentrx.net/staff/",
header: "Musical Staff",
thb: "notation",
w_h: "min-width:60vw;max-width:600px;height:650px",
reload: 0
},
{
link: "https://wsl.netcentrx.net/",
header: "WSL Commands",
thb: "wsl",
w_h: "min-width:60vw;max-width:600px;height:650px",
reload: 0
}
];
function createModalLinks(
link,
modalID,
header,
img,
w_h = "width:90vw;height:600px",
reload = "0"
) {
let modalLink = "";
modalLink = `
<a href="javascript:void(0)" class="modal-button" onclick="console.log('onclick handler:${link}');" data-header="${header}" data-target="${modalID}" data-iframe="${link}" data-dimensions="${w_h};margin-top:20px" data-reload="${reload}">
<img src="https://resume.netcentrx.net/examples/${img}.jpg" title="${img}" width="50">
</a>`;
return modalLink;
}
let theLinks = "";
modal_links.forEach((item) => {
theLinks += createModalLinks(
item.link,
"modal_window",
item.header,
item.thb,
item.w_h,
item.reload
);
});
main.innerHTML = theLinks;
_InitModal();
.modal-background {
font-family: sans-serif;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: none;
overflow: auto;
background-color: rgba(0, 0, 0, 0.9);
z-index: 9999;
background: rgba(55, 55, 55, 0.6);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.modal-window {
position: relative;
background-color: #ffffff;
width: 50%;
margin: 10% auto;
border-radius: 0.5rem;
padding: 0.75rem;
border: 1px groove #ccc;
/* box-shadow: 1px 1px 1px #999, 2px 2px 2px #000; */
}
.close-modal:hover,
.close-modal:focus {
color: rgba(255, 255, 255, 1);
cursor: pointer;
background: red;
transition: 1s;
text-shadow: 1px 1px 1px #999, 2px 2px 2px #000;
}
button.close-modal {
position: absolute;
top: -0.75rem;
right: -0.75rem;
padding: 0.05rem 0.75rem;
background: #999;
color: #ccc;
border-radius: 50%;
border: none;
outline: none;
-webkit-transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
-webkit-animation-name: animatebottom;
-webkit-animation-duration: 1.5s;
animation-name: animatebottom;
animation-duration: 1.5s;
}
button.close-modal::before {
content: "\D7";
font-size: 2rem;
}
.modal-window {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
-webkit-animation-name: animatetop;
-webkit-animation-duration: 0.5s;
animation-name: animatetop;
animation-duration: 0.5s;
}
.modal-header {
height: 30px;
text-align: center;
width: 100%;
background: #fff;
padding: 0.2rem;
}
.modal-header h1 {
font-size: 1.1rem;
}
.modal-footer {
height: 20px;
text-align: center;
width: 100%;
background: #fff;
padding: 0.2rem;
}
.modal-content {
background-color: #fff;
height: calc(100% - 70px);
border-radius: 0.5rem;
border: 0.1rem groove #ddd;
overflow: hidden;
}
.button-footer {
background: #fff;
border-radius: 0.5rem;
border: 1px outset #aaa;
padding: 0.2rem;
color: #999;
transition: 1s;
cursor: pointer;
}
.button-footer:hover {
background: #fdfdfd;
color: #555;
border: 1px inset #ddd;
text-shadow: 0.05rem 0.05rem 0.05rem #ccc, 0.055rem 0.055rem 0.055rem #999,
0.06rem 0.06rem 0.06rem #333;
transition: 1s;
}
.close-btn:hover {
color: white;
background: #f00;
cursor: pointer;
}
#modal_iframe {
width: 100%;
height: 100%;
}
button.modal-button {
border-radius: 0.5rem;
border: 0px solid #aaa;
padding: 0;
cursor: pointer;
}
.modal-button-img {
border-radius: 0.5rem;
border: 0.1rem groove #444;
cursor: pointer;
}
.sepia:hover {
filter: sepia(150%);
}
/*
.none {
display: none;
}
*/
#-webkit-keyframes animatetop {
from {
top: -300px;
opacity: 0;
}
to {
top: 0;
opacity: 1;
}
}
#keyframes animatetop {
from {
top: -300px;
opacity: 0;
}
to {
top: 0;
opacity: 1;
}
}
#-webkit-keyframes animatebottom {
from {
top: 0;
opacity: 1;
}
to {
bottom: -300px;
opacity: 0;
}
}
#keyframes animatebottom {
from {
top: 0;
opacity: 1;
}
to {
bottom: -300px;
opacity: 0;
}
}
.container {
border-radius: 0.5rem;
border: 1px solid #aaa;
max-width: 800px;
width: 500px;
margin: 0 auto;
text-align: center;
font-family: sans-serif;
}
main,
aside {
font-family: sans-serif;
max-width: 800px;
width: 500px;
margin: 0 auto;
text-align: center;
}
h2 {
text-align: center;
font-family: sans-serif;
font-weight: normal;
font-size: 1.2rem;
}
span {
font-size: 75%;
background: #ffff0055;
}
<div id="modal_window" class="modal-background">
<div class="modal-window">
<button class="close-modal" data-dismiss="modal"></button>
<div class="modal-header"></div>
<div class="modal-content">
<iframe src="#" id="modal_iframe" frameborder="0">If you'd have had a real browser, I wouldn't be boring you with this now...</iframe>
</div>
<div class="modal-footer"><button class="button-footer">Open In New Tab</button></div>
</div>
</div>
<div class="container">
<h2><code>main</code> Content Rendered By JavaScript</h2>
<main>
Main
</main>
<span>working now</span>
</div>
Im building an app where you can like and dislike movies. I have a database of movies and show each one to the user. When you click like or dislike it takes you to the next poster.
I also have access to the description and rating. I've added my own modal to show the user when clicked more information on that movie.
The problem Im having is the modal works for the first movie and changes the class to active. However on the second one it stops working. I can see in the inspector the modal code is there and has the correct information for that movie. But the modal seems to still not work/become unresponsive. Perhaps its an error that I've missed. I notice that it does add active once again on the first item. Perhaps I need to find a way to switch to the current movie instead or incorporate the modal into the slides more? or find a way to reset it..
EDIT -:
Adding to the above. Having done a bit more testing. It seems like the modal doesnt know what id the slide is on.. So it never knows when the slide is changed.. Any ideas how I would do this?
EDIT2 -:
I've now got it so every modal has a unique id per poster using the contact.id. Any way to link the modal id together with the slides so when I change the slide the modal knows and changes to active on the next slide?
const modalBtn = document.querySelectorAll(".modal-btn");
const modalBg = document.querySelectorAll("movie-modal");
const modalClose = document.querySelectorAll(".close-button");
const overlay = document.querySelectorAll("overlay");
const overlayClose = document.querySelectorAll("overlay");
const activeModal = document.querySelectorAll(".movie-modal:first-child")
modalBtn.forEach(function(btn, index){
btn.addEventListener('click', function(click) {
console.log(click)
modalBg.classList.add('active');
overlay.classList.add('active');
var content_id = activeModal.data("id");
console.log(content_id)
});
});
modalClose.forEach(function(btn, index){
btn.addEventListener('click', function(click) {
console.log(click)
modalBg.classList.remove('active');
overlay.classList.remove('active');
});
});
overlay.forEach(function(overlay, index){
overlay.addEventListener('click', function() {
overlayClose.classList.remove('active');
});
});
overlay.forEach(function(overlay, index){
overlay.addEventListener('click', function() {
const modals = document.querySelectorAll('.movie-modal.active')
modals.forEach(modal => {
modal.classList.remove('active');
overlay.classList.remove('active');
})
})
});
$(function(){
var $activeSlide = $('#slides .slide:first-child');
// show first slide
$activeSlide.addClass("active");
// on click event decline
$("#decline").on("click", function(){
goToSlide('decline');
});
// on click approve then what?
$("#approve").on("click", function(){
var content_id = $activeSlide.data("id");
console.log(content_id)
goToSlide('approve');
// $.ajax({
// url: "/user_contents/liked" + content_id,
// method: "post",
// dataType: "ajax"
// });
});
// adding the 'showing' or 'active' slide class to each element
function goToSlide(action) {
$activeSlide.removeClass("active");
$activeSlide = $activeSlide.next(".slide");
// send data to controller
if(action == "approve"){
console.log(action);
} else {
console.log('dislike');
}
$activeSlide.addClass("active");
}
});
.movie-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
transition: 200ms ease-in-out;
box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
border-radius: 10px;
z-index: 10;
background-color: white;
width: 300px;
max-width: 80%;
color: black;
}
.movie-modal.active{
transform: translate(-50%, -50%) scale(1);
}
.movie-modal-header {
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgb(182, 182, 182);
}
.movie-rating {
font-size: 1.25rem;
font-weight: bold;
}
.close-button {
cursor: pointer;
border: none;
outline: none;
background: none;
font-size: 1.25rem;
font-weight: bold;
}
.movie-modal-body {
padding: 16px;
color: rgb(160, 160, 160);
}
#overlay {
position: fixed;
opacity: 0;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.35);
pointer-events: none;
transition: 200ms ease-in-out;
}
#overlay.active {
opacity: 1;
pointer-events: all;
}
#slides {
position: relative;
height: 100%;
padding: 0px;
margin: 0 auto;
list-style-type: none;
}
.slide {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
opacity: 0;
z-index: 1;
box-sizing: border-box;
color: #fff;
-webkit-transition: opacity 1s;
-moz-transition: opacity 1s;
-o-transition: opacity 1s;
transition: opacity 1s;
}
.showing {
opacity: 1;
z-index: 2;
}
#slide-controls {
margin: 20px 0 400px;
width: 100%;
position: absolute;
display: flex;
justify-content: center;
justify-content: space-between;
z-index: 10;
color: #ffffffba;
font-size: 24px;
top: 50%;
}
.image-carousel {
width: 100%;
height: 642px;
margin: 0 auto;
background-size: contain;
background-repeat: no-repeat;
position: relative;
}
#decline {
margin-left: 16px;
}
#approve {
margin-right: 16px;
}
.img-thumbnail {
padding: 0 !important;
}
<ul id="slides">
<% #contents.each_with_index do |content, i| %>
<li class="slide <%= 'active' if i == 0 %>" data-id="<%= content.id %>">
<div class="image-carousel" style="background-image: url('<%= content.poster %>')">
</div>
<button class="modal-btn">Open Modal</button>
<div id="movie-modal <%= content.id %>" class="movie-modal">
<div class="movie-modal-header">
<div class="movie-rating" style="">Average rating: <strong><%= content.rating %></strong></div>
<button class="close-button">×</button>
</div>
<div class="movie-modal-body"><p><%= content.description %></p></div>
</div>
<div id="overlay <%= content.id %>"></div>
</li>
<% end %>
</ul>
<div id="slide-controls">
<span id="decline"><i class="fa fa-solid fa-thumbs-down fa-2x"></i></span>
<span id="approve"><i class="fa fa-solid fa-thumbs-up fa-2x"></i></span>
</div>
This shows when the first poster is clicked the modal works and movie-modal active is added to the modal class
The second image with Morbius in it.. The modal class doesnt change for the current slide showing however it changes for the previous slide.. BUT the previous slide only changes once and then sticks to active. Nothing is shown on the poster.
Looking at this codepen, most of it I grok. But a couple of things I don't understand:
How does the <code> element stay perfectly on top of the <textarea>? I would expect it to be below the textarea looking at the HTML code.
How is the cursor staying so well-aligned with the text such that it functions like the type of cursor in a word document? The cursor even aligns well with the text when I copy and paste the text. Is it the emmet dependency that's helping?
Here is the code:
HTML
<div class="editor-holder">
<ul class="toolbar">
<li><i class="fa fa-indent"></i></li>
<li><i class="fa fa-expand"></i></li>
</ul>
<div class="scroller">
<textarea class="editor allow-tabs"><div class="Editable Textarea">
<h1>This is a fully editable textarea which auto highlights syntax.</h1>
<p>Type or paste any code in here...</p>
<div>
<?php
var simple = "coding";
?>
<script>
with = "Tab or double space functionality";
</script></textarea>
<pre><code class="syntax-highight html"></code></pre>
</div>
</div>
(S)CSS
html, body{
margin: 0;
padding: 0;
height: 100%;
width: 100%;
position: relative;
background: rgb(114, 195, 195);
}
.editor-holder{
width: 800px;
height: 500px;
margin-top: 50px;
border-radius: 3px;
position: relative;
top: 0;
margin-left: -400px;
left: 50%;
background: #1f1f1f !important;
overflow: auto;
box-shadow: 5px 5px 10px 0px rgba(0, 0, 0, 0.4);
transition: all 0.5s ease-in-out;
&.fullscreen{
width: 100%;
height: 100%;
margin: 0;
left: 0;
}
.toolbar{
width: 100%;
list-style: none;
position: absolute;
top: -2px;
margin: 0;
left: 0;
z-index: 3;
padding: 8px;
background: #afafaf;
li{
display: inline-block;
}
a{
line-height: 20px;
background: rgba(144, 144, 144, 0.6);
color: grey;
box-shadow: inset -1px -1px 1px 0px rgba(0,0,0,0.28);
display: block;
border-radius: 3px;
cursor: pointer;
&:hover{
background: rgba(144, 144, 144, 0.8);
}
&.active{
background: rgba(144, 144, 144, 0.8);
box-shadow: none;
}
}
i{
color: #565656;
padding: 8px;
}
}
textarea, code{
width: 100%;
height: auto;
min-height: 450px;
font-size: 14px;
border: 0;
margin: 0;
top: 46px;
left: 0;
padding: 20px !important;
line-height: 21px;
position: absolute;
font-family: Consolas,Liberation Mono,Courier,monospace;
overflow: visible;
transition: all 0.5s ease-in-out;
}
textarea{
background: transparent !important;
z-index: 2;
height: auto;
resize: none;
color: #fff;
text-shadow: 0px 0px 0px rgba(0, 0, 0, 0);
text-fill-color: transparent;
-webkit-text-fill-color: transparent;
&::-webkit-input-placeholder{
color: rgba(255, 255, 255, 1);
}
&:focus{
outline: 0;
border: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
code{
z-index: 1;
}
}
pre {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
code{
background: #1f1f1f !important;
color: #adadad;
.hljs {
color: #a9b7c6;
background: #282b2e;
display: block;
overflow-x: auto;
padding: 0.5em
}
.hljs-number,
.hljs-literal,
.hljs-symbol,
.hljs-bullet {
color: #6897BB
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-deletion {
color: #cc7832
}
.hljs-variable,
.hljs-template-variable,
.hljs-link {
color: #629755
}
.hljs-comment,
.hljs-quote {
color: #808080
}
.hljs-meta {
color: #bbb529
}
.hljs-string,
.hljs-attribute,
.hljs-addition {
color: #6A8759
}
.hljs-section,
.hljs-title,
.hljs-type {
color: #ffc66d
}
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #e8bf6a
}
.hljs-emphasis {
font-style: italic
}
.hljs-strong {
font-weight: bold
}
}
}
JavaScript
var tabCharacter = " ";
var tabOffset = 2;
$(document).on('click', '#indent', function(e){
e.preventDefault();
var self = $(this);
self.toggleClass('active');
if(self.hasClass('active'))
{
tabCharacter = "\t";
tabOffset = 1;
}
else
{
tabCharacter = " ";
tabOffset = 2;
}
})
$(document).on('click', '#fullscreen', function(e){
e.preventDefault();
var self = $(this);
self.toggleClass('active');
self.parents('.editor-holder').toggleClass('fullscreen');
});
/*------------------------------------------
Render existing code
------------------------------------------*/
$(document).on('ready', function(){
hightlightSyntax();
emmet.require('textarea').setup({
pretty_break: true,
use_tab: true
});
});
/*------------------------------------------
Capture text updates
------------------------------------------*/
$(document).on('ready load keyup keydown change', '.editor', function(){
correctTextareaHight(this);
hightlightSyntax();
});
/*------------------------------------------
Resize textarea based on content
------------------------------------------*/
function correctTextareaHight(element)
{
var self = $(element),
outerHeight = self.outerHeight(),
innerHeight = self.prop('scrollHeight'),
borderTop = parseFloat(self.css("borderTopWidth")),
borderBottom = parseFloat(self.css("borderBottomWidth")),
combinedScrollHeight = innerHeight + borderTop + borderBottom;
if(outerHeight < combinedScrollHeight )
{
self.height(combinedScrollHeight);
}
}
// function correctTextareaHight(element){
// while($(element).outerHeight() < element.scrollHeight + parseFloat($(element).css("borderTopWidth")) + parseFloat($(element).css("borderBottomWidth"))) {
// $(element).height($(element).height()+1);
// };
// }
/*------------------------------------------
Run syntax hightlighter
------------------------------------------*/
function hightlightSyntax(){
var me = $('.editor');
var content = me.val();
var codeHolder = $('code');
var escaped = escapeHtml(content);
codeHolder.html(escaped);
$('.syntax-highight').each(function(i, block) {
hljs.highlightBlock(block);
});
}
/*------------------------------------------
String html characters
------------------------------------------*/
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
/*------------------------------------------
Enable tabs in textarea
------------------------------------------*/
$(document).delegate('.allow-tabs', 'keydown', function(e) {
var keyCode = e.keyCode || e.which;
if (keyCode == 9) {
e.preventDefault();
var start = $(this).get(0).selectionStart;
var end = $(this).get(0).selectionEnd;
// set textarea value to: text before caret + tab + text after caret
$(this).val($(this).val().substring(0, start)
+ tabCharacter
+ $(this).val().substring(end));
// put caret at right position again
$(this).get(0).selectionStart =
$(this).get(0).selectionEnd = start + tabOffset;
}
});
How does code and textarea elements stay aligned?
They are aligned because in the CSS they both use the same style which outlines the positioning of both elements. They both use position: absolute and the same top and left properties so stay in the same position.
See here https://www.w3schools.com/css/css_positioning.asp
In terms of the z-axis you can see that code has a z-index of 1 while textarea has 2 so text area stays on top.
How is the cursor staying aligned with the text?
There is no javascript acting on the cursor here. If you were to have any textarea in html the cursor would align with the text.
We use custom selectbox elements for our page (for reasons), but in one specific place and only with IE (in my case version 10 and 11), there is a problem.
When you open the box and close it, sometimes the browser leaves visual artifacts of that element. If there is a button below the element (in the opened state), then those artifacts are still on top of that button UNTIL you hover over the button, then those artifacts disappear at that place.
I think everyone experienced this behaviour at some point in his life with windows or something else. Sorry if my description isn't perfect.
Here are some screenshots (The red boxes are censored text, sorry):
Open state:
Closed state with artifacts:
Hope my description is accurate, I desperately need a fix for this.
I can't reproduce it on my system, but our QA guy can reproduce it consistently. It also changes depending on the size of the browser. On some sizes it doesn't seem to happen at all. It must be some weird rendering glitch of IE.
I don't think it has anything to do with our custom selectbox element, because it happens only in this specific place and in our old design we experienced it in a different place with a completely different element. At that time I thought it's because the DOM is too complicated for IE, but this page has almost no elements.
EDIT: I Just found out that if I minimize the window and maximize it again, the artifacts disappear, which should confirm that this is a rendering glitch, right?
EDIT 2: Here is the code and css of our custom selectbox
HTML:
<div class="input_selectbox">
<label>Title</label>
<div class="input_selectbox__head" data-placeholder="">
<label>
<input type="hidden" name="id" value="5-10909">
Dummy 1
</label>
</div>
<div class="input_selectbox__body">
<label data-value="" class="">
No Option
</label>
<label id="5-10909" data-value="5-10909" class="input_selectbox__option--selected" data-default="true">
Dummy 1
</label>
<label id="5-12568" data-value="5-12568" class="">
Dummy 2
</label>
<label id="5-20001" data-value="5-20001" class="">
Dummy 3
</label>
<label id="5-20002" data-value="5-20002" class="">
Dummy 4
</label>
</div>
</div>
LESS:
.input_combobox, .input_selectbox {
display: block;
font-family: RobotoCondensed-Regular;
font-size: 14px;
width: 100%;
overflow: hidden;
&:focus {
outline: none;
}
label {
cursor: pointer;
}
}
.input_selectbox {
font-family: Roboto-Regular;
}
.input_selectbox__head {
border: 1px solid #colorBorderGrey;
transition: background-color 0.2s, border 0.3s;
overflow-x: hidden;
text-overflow: ellipsis;
display: flex;
background-color: #colorLightestGrey;
padding: 10px;
justify-content: space-between;
&:hover {
cursor: pointer;
border-color: #colorDarkGrey;
&:after {
color: #colorBlue;
}
}
label {
display: flex;
user-select: none;
pointer-events: none;
i {
margin-right: 10px;
}
}
&:after {
content: "\f078";
font-style: normal;
font-variant: normal;
text-rendering: auto;
font-family: "Font Awesome 5 Pro";
font-weight: 900;
pointer-events: none;
transition: color 0.3s, opacity 0.3s, background-color 0.2s;
display: block;
padding-left: 10px;
font-size: 16px;
}
}
.input_combobox--active, .input_selectbox--active {
.input_combobox__head, .input_selectbox__head {
background-color: #colorLightGrey;
&:hover {
border: 1px solid #colorBorderGrey;
&:after {
color: #colorDarkGrey;
}
}
&:after {
background-color: #colorLightGrey;
}
span {
background-color: #colorGreyHover;
}
}
}
.input_combobox__body, .input_selectbox__body {
display: none;
position: fixed;
box-shadow: rgba(0,0,0, 0.05) 0px 2px 5px 0px;
background-color: #colorWhite;
border: 1px solid #colorBorderGrey;
border-top: none;
max-height: 400px;
overflow-y: auto;
width: 100%;
z-index: 499;
label {
font-family: RobotoCondensed-Regular;
font-size: 16px;
display: block;
padding: 10px 20px;
transition: background-color 0.5s, color 0.3s;
border-bottom: 1px solid #colorBorderGrey;
&:hover {
background-color: #colorLightGrey;
}
&:last-child {
border-bottom: none;
}
i {
margin-right: 10px;
}
input[type=checkbox] {
display: none;
}
}
}
.input_combobox__body--top, .input_selectbox__body--top {
box-shadow: rgba(0, 0, 0, 0.1) 0px -2px 5px 0px;
}
.input_combobox__option--inactive, .input_selectbox__option--inactive {
opacity: 0.3;
}
Javascript:
function selectbox()
{
// click
$(document).on("click", ".input_selectbox__head", function ()
{
$(this).trigger("selectbox:toggle");
});
// toggle
$(document).on("selectbox:toggle", ".input_selectbox__head", function ()
{
if ($(this).parent().hasClass("input_selectbox--active"))
{
$(this).trigger("selectbox:close");
}
else
{
$(this).trigger("selectbox:open");
}
});
// open
$(document).on("selectbox:open", ".input_selectbox__head", function ()
{
var selectbox = $(this).closest(".input_selectbox");
if (!selectbox.hasClass("readonly") && !selectbox.hasClass("input--disabled"))
{
$(".input_selectbox--active .input_selectbox__head").trigger("selectbox:close");
// Positionierung
var header = selectbox.find(".input_selectbox__head");
var headerHeight = header.outerHeight();
var selectboxBody = selectbox.find(".input_selectbox__body");
var headerPositionX = header.offset().left;
var headerPositionY = header.offset().top - $(window).scrollTop();
var bodyPositionY = headerPositionY + headerHeight;
selectboxBody.removeClass("input_selectbox__body--top");
selectboxBody.css({
"top": bodyPositionY,
"left": headerPositionX,
"width": selectbox.width(),
"bottom": ""
});
selectbox.addClass("input_selectbox--active");
selectboxBody.show();
// check if offscreen
var isOut = isOutOfViewport(selectboxBody.get(0));
if (isOut.bottom)
{
selectboxBody.addClass("input_selectbox__body--top");
selectboxBody.css({
top: "",
bottom: ($(window).innerHeight() - headerPositionY - 1)
});
}
// close combobox on parent scroll
var scrollParent = getScrollParent(header[0]);
$(scrollParent).one("scroll", function ()
{
header.trigger("selectbox:close");
});
$(document).one("scroll", function ()
{
header.trigger("selectbox:close");
});
$(window).one("resize", function ()
{
header.trigger("selectbox:close");
});
}
});
// close
$(document).on("selectbox:close", ".input_selectbox__head", function ()
{
var selectbox = $(this).closest(".input_selectbox");
selectbox.removeClass("input_selectbox--active");
var selectboxBody = selectbox.find(".input_selectbox__body");
selectboxBody.hide();
});
// change option
$(document).on("click", ".input_selectbox__body > label", function ()
{
var label = $(this);
var value = label.attr("data-value");
var headerLabel = $(this).closest(".input_selectbox__body").siblings(".input_selectbox__head").children("label");
var name = headerLabel.find("input[type=hidden]").attr("name");
label.addClass("input_selectbox__option--selected").siblings().removeClass("input_selectbox__option--selected");
headerLabel.html(label.html());
headerLabel.append('<input type="hidden" name="' + name + '" value="' + value + '" />');
headerLabel.closest(".input_selectbox__head").trigger("selectbox:close");
$(this).closest(".input_selectbox").trigger("selectbox:change").trigger("change");
});
// close selectbox on outside click
$(document).ready(function ()
{
$(document).on("click touchstart", function (event)
{
if ($(event.target).closest('.input_selectbox--active').length == 0)
{
$(".input_selectbox--active .input_selectbox__head").trigger("selectbox:close");
}
});
});
// form reset
$(document).on("reset", "form", function ()
{
var selectboxes = $(this).find(".input_selectbox");
selectboxes.each(function ()
{
$(this).find(".input_selectbox__body").find("label[data-default=true]").click();
});
});
}
I'm currently setting up a news/image slider on my site via JS. I have the slide data rolling in through a PHP loop with unique ID's. Everything is working smoothly, I just can't figure out how to reset the timer/interval when you manually switch slides.
Also, there has to be a better/easier way to write the manual click navigation I currently have setup with all the unique ID's. I have the loop sliced at 5.
(my code is a mess)
$(document).ready(function(){
$("#newsFeatured article:first").addClass("active");
$("#newsFeatured li:first").addClass("active");
});
var toggleSlide = function(){
$("#newsFeatured article.active").removeClass("active")
.next().add("#newsFeatured article:first").last().addClass("active");
$("#newsFeatured li.active").removeClass("active")
.next().add("#newsFeatured li:first").last().addClass("active");
}
setInterval(toggleSlide, 8000);
$(document).ready(function(){
$("#control1").on('click', function() {
$("#slide1").addClass("active");
$("#slide2, #slide3, #slide4, #slide5").removeClass("active");
$("#control1").addClass("active");
$("#control2, #control3, #control4, #control5").removeClass("active");
clearInterval(toggleSlide);
});
$("#control2").on('click', function() {
$("#slide2").addClass("active");
$("#slide1, #slide3, #slide4, #slide5").removeClass("active");
$("#control2").addClass("active");
$("#control1, #control3, #control4, #control5").removeClass("active");
});
$("#control3").on('click', function() {
$("#slide3").addClass("active");
$("#slide1, #slide2, #slide4, #slide5").removeClass("active");
$("#control3").addClass("active");
$("#control1, #control2, #control4, #control5").removeClass("active");
});
$("#control4").on('click', function() {
$("#slide4").addClass("active");
$("#slide1, #slide2, #slide3, #slide5").removeClass("active");
$("#control4").addClass("active");
$("#control1, #control2, #control3, #control5").removeClass("active");
});
$("#control5").on('click', function() {
$("#slide5").addClass("active");
$("#slide1, #slide2, #slide3, #slide4").removeClass("active");
$("#control5").addClass("active");
$("#control1, #control2, #control3, #control4").removeClass("active");
});
});
https://jsfiddle.net/aor1xmb5/
Lastly, i'm interested in getting my slide to interact with touch for mobile devices, if anyone can point me in the direction of a good tutorial on getting that started.
Thanks!
Clearing intervals is fairly simple:
function myFn() {console.log('idle');}
var myTimer = setInterval(myFn, 4000);
// Then, later at some future time,
// to restart a new 4 second interval starting at this exact moment in time
clearInterval(myTimer);
myTimer = setInterval(myFn, 4000);
Please check the snippet:
$(function() {
$("#newsFeatured article:first").addClass("active");
$("#newsFeatured li:first").addClass("active");
var sliderInterval = setInterval(toggleSlide, 8000);
$('.featuredControls').on('click', 'li', function() {
var $this = $(this),
id = $this.attr('id'),
index = id.replace('control', '');
slideTo(index);
// Clear interval.
clearInterval(sliderInterval);
sliderInterval = setInterval(toggleSlide, 8000);
});
function slideTo(index) {
var id = '#control' + index,
$this = $(id);
// Highlight active slide.
$(".featuredSlide").removeClass("active");
$("#slide" + index).addClass("active");
// Highlight active control.
$this.parent().find('li').removeClass("active");
$this.addClass("active");
}
function toggleSlide() {
// Get current slide.
var id,
index,
$next = $(".featuredControls .active").next();
// If last item, start over.
if ($next.length === 0) {
$next = $(".featuredControls li").first();
}
id = $next.attr('id'),
index = id.replace('control', '');
slideTo(index);
};
});
/* NEWS FEATURED SLIDER */
#newsFeatured {
position: relative;
height: 300px;
transition: 0.1s all linear;
}
#newsFeatured:hover {
box-shadow: -6px 0px 0px 0px #ffc60d;
}
.featuredControls {
opacity: 0;
position: absolute;
list-style-type: none;
right: 30px;
margin: 0;
padding: 20px;
z-index: 1;
transition: 0.2s all linear;
}
#newsFeatured:hover .featuredControls {
opacity: 1;
right: 0;
}
.featuredControls li {
background: rgba(0, 0, 0, 0.7);
display: inline-block;
height: 20px;
width: 15px;
border: 0;
border-radius: 3px;
cursor: pointer;
}
.featuredControls li.active {
background: #ffc60d;
}
.featuredSlide {
display: none;
background: rgba(0, 0, 0, 0.3);
position: absolute;
left: 0;
width: 100%;
height: 300px;
overflow: hidden;
}
#newsFeatured:hover .featuredSlide {
box-shadow: -1px 0px 0px 0px #101415;
}
#newsFeatured article.active {
display: block;
}
.featuredImage {
width: 100%;
height: 100%;
background-size: cover;
background-position: 50% 50%;
background-repeat: no-repeat;
transition: 0.3s all ease;
animation: featuredImage ease 1;
animation-duration: 1s;
}
#keyframes featuredImage {
from {
opacity: 0;
background-position: 30% 50%;
}
to {
opacity: 1;
background-position: 50% 50%;
}
}
.featuredContent {
width: 100%;
padding: 20px;
background: rgba(0, 0, 0, 0.65);
position: absolute;
bottom: 0;
transition: 0.5s all ease;
}
.featuredContent h2 {
font-size: 16px;
font-weight: normal;
text-transform: uppercase;
margin: 0;
animation: featuredTitle ease 1;
animation-duration: 1s;
}
#keyframes featuredTitle {
from {
padding-left: 75px;
opacity: 0;
}
to {
padding-left: 0;
opacity: 1;
}
}
.featuredContent h2 a {
color: #ffc60d;
margin: 0 0 5px 0;
transition: 0.1s all linear;
}
#newsFeatured:hover .featuredContent h2 a {
color: #eee;
}
.featuredContent section {
color: #a7a397;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id='newsFeatured' class='ipsClearfix'>
<ul class='featuredControls'>
<li id='control1'></li>
<li id='control2'></li>
</ul>
<article id='slide1' class='featuredSlide'>
<a href=''>
<div class='featuredImage' style='background-image: url(http://i.imgur.com/udTA5il.jpg);'></div>
</a>
<div class='featuredContent'>
<h2>
First Slide Title
</h2>
<section class='ipsType_normal ipsType_richText ipsType_break'>First slide description.</section>
</div>
</article>
<article id='slide2' class='featuredSlide'>
<a href=''>
<div class='featuredImage' style='background-image: url(http://i.imgur.com/SWy0AHZ.jpg);'></div>
</a>
<div class='featuredContent'>
<h2>
Second Slide Title
</h2>
<section class='ipsType_normal ipsType_richText ipsType_break'>Second slide description.</section>
</div>
</article>
</div>