I am working on a website that can generate templates with drag and drop. I have made an element generator. But the problem is that I can only drag and drop the elements that are already in the "container" as an example " already button" but the generated items are not droppable.
I'm new to JavaScript so don't know much about it. Please solve this problem. So I may continue to work on my website Here is my code
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
body {
margin: 0;
}
.container {
background-color: #333;
padding: 1rem;
margin-top: 1rem;
}
.draggable {
padding: 1rem;
background-color: white;
border: 1px solid black;
cursor: move;
}
.draggable.dragging {
opacity: .5;
}
/* Background Styles Only */
#import url('https://fonts.googleapis.com/css?family=Raleway');
* {
font-family: Raleway;
}
.side-links {
position: absolute;
top: 15px;
right: 15px;
}
.side-link {
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
margin-bottom: 10px;
color: white;
width: 180px;
padding: 10px 0;
border-radius: 10px;
}
.side-link-youtube {
background-color: red;
}
.side-link-twitter {
background-color: #1DA1F2;
}
.side-link-github {
background-color: #6e5494;
}
.side-link-text {
margin-left: 10px;
font-size: 18px;
}
.side-link-icon {
color: white;
font-size: 30px;
}
</style>
<body>
<textarea id="ambtnhtml" name="generatedCode1" class="generatedCode1"> <button type="button" class="draggable" id="" draggable ="true"> Button</button></textarea>
<!-- area -->
<button type="button" id="generatehtml">generate button </button>
<div class="container pipp" style="margin: auto;">
<button type="button" class="draggable AM-btnhj" id="" draggable="true">Already Button</button>
</div>
<div class="container">
</div>
</body>
<!-- drag and drop able script -->
<script>
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
containers.forEach(container => {
container.addEventListener('dragover', e => {
e.preventDefault()
const afterElement = getDragAfterElement(container, e.clientY)
const draggable = document.querySelector('.dragging')
if (afterElement == null) {
container.appendChild(draggable)
} else {
container.insertBefore(draggable, afterElement)
}
})
})
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return {
offset: offset,
element: child
}
} else {
return closest
}
}, {
offset: Number.NEGATIVE_INFINITY
}).element
}
</script>
<!-- add new button -->
<script src="http://code.jquery.com/jquery-1.8.1.min.js"></script>
<script>
$(document).ready(function() {
$("#generatehtml").click(function() {
$(".pipp").append($("#ambtnhtml").val());
$("ambtnhtml").val("");
});
});
</script>
</html>
Please Help
You missed adding event handlers to the new elements. I modified your example by adding event handlers to a separate function initDraggable so that later it would be easier to use it when generating new elements.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
body {
margin: 0;
}
.container {
background-color: #333;
padding: 1rem;
margin-top: 1rem;
}
.draggable {
padding: 1rem;
background-color: white;
border: 1px solid black;
cursor: move;
}
.draggable.dragging {
opacity: .5;
}
/* Background Styles Only */
#import url('https://fonts.googleapis.com/css?family=Raleway');
* {
font-family: Raleway;
}
.side-links {
position: absolute;
top: 15px;
right: 15px;
}
.side-link {
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
margin-bottom: 10px;
color: white;
width: 180px;
padding: 10px 0;
border-radius: 10px;
}
.side-link-youtube {
background-color: red;
}
.side-link-twitter {
background-color: #1DA1F2;
}
.side-link-github {
background-color: #6e5494;
}
.side-link-text {
margin-left: 10px;
font-size: 18px;
}
.side-link-icon {
color: white;
font-size: 30px;
}
</style>
<body>
<textarea id="ambtnhtml" name="generatedCode1" class="generatedCode1"> <button type="button" class="draggable" id="" draggable ="true"> Button</button></textarea>
<!-- area -->
<button type="button" id="generatehtml">generate button </button>
<div class="container pipp" style="margin: auto;">
<button type="button" class="draggable AM-btnhj" id="" draggable="true">Already Button</button>
</div>
<div class="container">
</div>
</body>
<!-- drag and drop able script -->
<script>
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
function initDraggable (draggable) {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
}
draggables.forEach(initDraggable)
containers.forEach(container => {
container.addEventListener('dragover', e => {
e.preventDefault()
const afterElement = getDragAfterElement(container, e.clientY)
const draggable = document.querySelector('.dragging')
if (afterElement == null) {
container.appendChild(draggable)
} else {
container.insertBefore(draggable, afterElement)
}
})
})
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return {
offset: offset,
element: child
}
} else {
return closest
}
}, {
offset: Number.NEGATIVE_INFINITY
}).element
}
</script>
<!-- add new button -->
<script src="http://code.jquery.com/jquery-1.8.1.min.js"></script>
<script>
$(document).ready(function() {
$("#generatehtml").click(function() {
const newDraggable = $($("#ambtnhtml").val())
initDraggable(newDraggable.get(0))
$(".pipp").append(newDraggable);
$("ambtnhtml").val("");
});
});
</script>
</html>
let myBtn = document.querySelector(".mainBtn");
let myManu = document.querySelector(".manu");
let manuOpen = false;
myManu.style.marginTop="-200px";
function menuFunction() {
if(manuOpen == false) { myManu.style.marginTop = "0px";
manuOpen = true;
}
else if (manuOpen == true) {
myManu.style.marginTop = "-200px";
manuOpen = false;
}
}
myBtn.onclick = menuFunction;
.mainBtn {
width: 40px;
height: 40px;
background: red;
z-index: 100;
margin-left: 10px;
cursor: pointer;
}
.mainBtn:hover {
background: yellow;
}
.social {
margin-right: 10px;
z-index: 100;
}
.manuSection {
max-width: 1080px;
width: 100%;
padding: 20px 0 20px 0;
float: right;
text-align: right;
}
.manu {
top: 40px;
max-width:1080px;
width: 100%;
position: absolute;
background: black;
transition: ease-in-out .5s;
}
.manu>ul>li, .manu>ul>li>a{
list-style: none;
text-decoration: none;
color: white;
}
.menuItems{
display: flex;
background: rgba(0,0,0,0.4);
justify-content: center;
margin: 2px;
padding: 7px;
}
.menuItems:hover{
background: rgba(134,134,134,0.32);
}
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/style.css">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght#400;500&display=swap" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(function(){
$("header").load("asserts/header.html");
$("footer").load("asserts/footer.html");
});
</script>
</head>
<body>
<header></header>
when the header was in the index.html file the js "menuFunction" was working properly, however, I thought of load the header and the footer from a different folder to each page and took the header piece and loaded to the "header" section..! and finding the js function is throwing an error message..""Uncaught TypeError: Cannot read property 'style' of null",".
the header code is
<div class="headerItems">
<div class="manuSection">
<div class="socialAndBtn">
<div class="mainBtn"></div>
<div class="social">
<img src="https://picsum.photos/40/40?random=1" alt="">
<img src="https://picsum.photos/40/40?random=2" alt="">
<img src="https://picsum.photos/40/40?random=3" alt="">
</div>
</div>
<div class="manu">
<ul>
<li><span class="menuItems">Home</span></li>
<li><span class="menuItems">Services</span></li>
<li><span class="menuItems">Products</span></li>
<li><span class="menuItems">Contact</span></li>
</ul>
</div>
</div>
<div class="title"><h1>My Business Name</h1></div>
<div class="telephone"><h2>416 999 9999</h2></div>
</div>
When the browser reads the js code, especially document.querySelector(".mainBtn") and document.querySelector(".manu") the content of the header has not been loaded yet, hence the error message. load method can take a callback function as second argument, this function will be executed once the file has been loaded. My advise is to move your js code within this callback, like so…
$("header").load("asserts/header.html", function () {
let myBtn = document.querySelector(".mainBtn");
let myManu = document.querySelector(".manu");
let manuOpen = false;
myManu.style.marginTop = "-200px";
myBtn.onclick = menuFunction;
function menuFunction() {
if (manuOpen == false) {
myManu.style.marginTop = "0px";
manuOpen = true;
} else if (manuOpen == true) {
myManu.style.marginTop = "-200px";
manuOpen = false;
}
}
});
I need to add a click event to a css class so when clicked it switches to a different class. Specifically, I have a character class on li items that I would like to change to another class when the li item is clicked. Code:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<link rel="stylesheet" href="app.css" />
</head>
<body>
<div class="container">
<h1>Client Search</h1>
<div id="searchWrapper">
<input
type="text"
name="searchBar"
id="searchBar"
placeholder="search for a character"
onkeyup="myFunction()"
/>
</div>
<ul id="charactersList"></ul>
</div>
<script src="app.js"></script>
</body>
</html>
css
body {
font-family: sans-serif;
background-color: #111d4a;
}
* {
box-sizing: border-box;
}
h1 {
color: #eee;
margin-bottom: 30px;
}
.container {
padding: 40px;
margin: 0 auto;
max-width: 1000px;
text-align: center;
}
#charactersList {
padding-inline-start: 0;
display: none;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
grid-gap: 20px;
}
.character {
list-style-type: none;
background-color: #eaeaea;
border-radius: 3px;
padding: 10px 20px;
display: grid;
grid-template-columns: 3fr 1fr;
grid-template-areas:
'name image'
'house image';
text-align: left;
}
.character:hover {
background-color: blue;
cursor: pointer;
}
.character > h2 {
grid-area: name;
margin-bottom: 0px;
}
.character > p {
grid-area: house;
margin: 0;
}
#searchBar {
width: 100%;
height: 32px;
border-radius: 3px;
border: 1px solid #eaeaea;
padding: 5px 10px;
font-size: 12px;
}
#searchWrapper {
position: relative;
}
#searchWrapper::after {
content: '🔍';
position: absolute;
top: 7px;
right: 15px;
}
javaScript
const charactersList = document.getElementById('charactersList');
const searchBar = document.getElementById('searchBar');
let clientNames = [];
searchBar.addEventListener('keyup', (e) => {
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clientNames.filter((character) => {
return (
character.name.toLowerCase().includes(searchString) ||
character.house.toLowerCase().includes(searchString)
);
});
displayCharacters(filteredCharacters);
});
const loadCharacters = async () => {
try {
const res = await fetch('https://hp-api.herokuapp.com/api/characters');
clientNames = await res.json();
displayCharacters(hpCharacters);
} catch (err) {
console.error(err);
}
};
const displayCharacters = (characters) => {
const htmlString = characters
.map((character) => {
return `
<li class="character">
<h2>${character.name}</h2>
<p>House: ${character.house}</p>
</li>
`;
})
.join('');
charactersList.innerHTML = htmlString;
};
loadCharacters();
//change the display of characterListfrom none to grid
function myFunction() {
var charactersList = document.getElementById("charactersList");
charactersList.style.display = "grid";
//also check if searchBar is empty and set display back to none
var searchBar = document.getElementById("searchBar").value;
if (searchBar === ""){
charactersList.style.display = "none";
}
}
Great question!
I just made a CodePen to illustrate how to programmatically change CSS class names when a li item is clicked. Check out this example and let me know if this clarifies the problem : )
JS
let list = document.getElementById('myList');
let items = ['First', 'Second', 'Third', 'Fourth', 'Fifth'];
for(let item of items){
let li = document.createElement("LI");
li.appendChild(document.createTextNode(item));
li.classList.add('blue');
li.addEventListener("click", () => {
li.classList.remove('blue');
li.classList.add('red');
});
list.appendChild(li);
}
HTML
<ul id="myList"></ul>
CSS
.blue{
color: blue;
}
.red{
color: red;
}
https://codepen.io/CrowlsYung/pen/eYJRPjx
Suggested Change
<li class="character" onclick="handleItemClick(event)">
<h2>${character.name}</h2>
<p>House: ${character.house}</p>
</li>
function handleItemClick(e){
e.currentTarget.classList.toggle('prevClass')
e.currentTarget.classList.toggle('newClass');
}
I am not quite sure how to phrase my question, so please forgive me. My plan was to create giant images of letters that make up the words "Hello World". I wanted to have these words nest inside of the big boxes and later decided to have each word be inside a smaller box. In the picture, I have created a small box (the sized has not been permanently set, I was just testing). But when I created the second small box, it flew out of the big box. In my index.html file, the <div> for the second small box was nested inside the big box div.
Here is the code:
INDEX.HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>repl.it</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<script src="script.js"></script>
<div class = "box">
<div class = "hello-box"></div>
<div class = "h-left"></div>
<div class = "h-mid"></div>
<div class = "h-right"></div>
</div>
<div class = "world-box">
</div>
</div>
</body>
</html>
STYLE.CSS
body {
background-color: skyblue;
}
.box {
position: relative;
margin: auto;
margin-top: 15%;
display: block;
border: 3px solid black;
width: 800px;
height: 500px;
}
.hello-box {
position: relative;
margin: auto;
margin-top: 125px;
width: 100px;
height: 100px;
border: 1px solid black;
}
.world-box {
position: relative;
margin: auto;
margin-top: 125px;
width: 100px;
height: 100px;
border: 1px solid black;
}
RESULT
Any help would very appreciated!!!
.flex-container {
display: flex;
flex-wrap: nowrap;
background-color: DodgerBlue;
}
.flex-container > div {
background-color: #f1f1f1;
width: 100px;
margin: 10px;
text-align: center;
line-height: 75px;
font-size: 30px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>repl.it</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<script src="script.js"></script>
<div class="flex-container">
<div>H</div>
<div>E</div>
<div>L</div>
<div>L</div>
<div>O</div>
<div>W</div>
<div>O</div>
<div>R</div>
<div>L</div>
<div>D</div>
<div>!</div>
</div>
</body>
</html>
using flexbox
Assuming you're able to modify the HTML, the easiest way to fix this would be to simply shift your .world-box <div> to be inside of your .box <div>:
body {
background-color: skyblue;
}
.box {
position: relative;
margin: auto;
margin-top: 15%;
display: block;
border: 3px solid black;
width: 800px;
height: 500px;
}
.hello-box {
position: relative;
margin: auto;
margin-top: 125px;
width: 100px;
height: 100px;
border: 1px solid black;
}
.world-box {
position: relative;
margin: auto;
margin-top: 125px;
width: 100px;
height: 100px;
border: 1px solid black;
}
<body>
<div class="box">
<div class="hello-box"></div>
<div class="h-left"></div>
<div class="h-mid"></div>
<div class="h-right"></div>
<div class="world-box"></div>
</div>
</body>
I would actually create a function that would put anything you want into some kind of wrapper, like so:
/* js/external.js */
//<![CDATA[
var doc, bod, M, I, S, Q, special, unspecial, shuffle, ReaderBoard, autoBoard, randBoard; // for use on other loads
addEventListener('load', function(){
doc = document; bod = doc.body;
M = function(tag){
return doc.createElement(tag);
}
I = function(id){
return doc.getElementById(id);
}
S = function(selector, within){
var w = within || doc;
return w.querySelector(selector);
}
Q = function(selector, within){
var w = within || doc;
return w.querySelectorAll(selector);
}
special = function(str){
return str.replace(/&/g, '&').replace(/'/g, ''').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
}
unspecial = function(str){
return str.replace(/&/g, '&').replace(/'/g, "'").replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
}
shuffle = function(array){
var a = array.slice(), l = a.length;
a.sort(function(b, c){
return 0.5 - Math.random();
});
return a;
}
ReaderBoard = function(outputElement){
this.output;
var t = this;
this.setOutput = function(element){
this.output = element;
return this;
}
this.setOutput(outputElement);
this.setText = function(text){
this.output.innerHTML = '';
for(var i=0,a=text.split(''),s,d,l=a.length; i<l; i++){
d = M('div'); s = a[i];
if(s === ' ')d.className = 'space';
d.innerHTML = special(s); this.output.appendChild(d);
}
return this;
}
}
autoBoard = function(outputElement, textArray, interval, noLoop){
var rb = new ReaderBoard(outputElement), i = 0, r, l = textArray.length;
var v = interval || 1500;
rb.setText(textArray[0]);
r = setInterval(function(){
i++;
if(i === l){
if(noLoop){
clearInterval(r); r = undefined;
return;
}
else{
i = 0;
}
}
rb.setText(textArray[i]);
}, v);
}
randBoard = function(outputElement, textArray, interval, noLoop){
var rb = new ReaderBoard(outputElement), i = 0, r, a = shuffle(textArray), l = a.length;
var v = interval || 1500;
rb.setText(a[0]);
r = setInterval(function(){
i++;
if(i === l){
if(noLoop){
clearInterval(r); r = undefined;
return;
}
else{
a = shuffle(a); i = 0;
}
}
rb.setText(a[i]);
}, v);
}
var rb = new ReaderBoard(I('readerboard'));
rb.setText('Hello World!');
autoBoard(I('autoboard'), ['Check this out!', "It's a reader board.", 'This one runs in order.', 'Are you not amazed?']);
autoBoard(I('noloop_autoboard'), ['Check this out!', "It's a reader board.", 'This one runs in order.', 'Are you not amazed?'], 2000, true);
randBoard(I('randboard'), ['This is Awesome!', "Isn't totally random.", 'Create Something Cool', 'Success!', 'Nice']);
randBoard(I('noloop_randboard'), ['This is Awesome!', "Isn't totally random.", 'Create Something Cool', 'Success!', 'Nice'], 3500, true);
});
//]]>
/* css/external.css */
*{
box-sizing:border-box; padding:0; margin:0;
}
html,body{
width:100%; height:100%;
}
body{
background:#ccc;
}
.flex_text{
display:flex;
}
.flex_text>div{
flex:1; background:#fff; font:bold 30px Arial, Helvetica, sans-serif; text-align:center; margin:3px;
}
.flex_text>.space{
background:#ccc; margin:0;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1' />
<title>Test Template</title>
<link type='text/css' rel='stylesheet' href='css/external.css' />
<script type='text/javascript' src='js/external.js'></script>
</head>
<body>
<div class='flex_text' id='readerboard'></div>
<hr />
<div class='flex_text' id='autoboard'></div>
<hr />
<div class='flex_text' id='noloop_autoboard'></div>
<hr />
<div class='flex_text' id='randboard'></div>
<hr />
<div class='flex_text' id='noloop_randboard'></div>
</body>
</html>
Just a thought.
I am trying to build a simple toggle button using JavaScript, not jQuery, to translate the element back and forth. I did my best a newbie (for JavaScript) but cannot figure it out. The main idea is that the side menu should show from the side when clicked on the button and should hide back to the same place when it is clicked again.
Please help.
function toggleNavBar() {
var nav = document.getElementById("sideNav"),
button = document.getElementById("checkButton"),
check = "hidden";
if (check == "hidden") {
nav.style.transform = "translate(0px)";
check = "shown";
} else if (check == "shown") {
nav.style.transform = "translate(-290px)";
check = "hidden";
}
}
body {
margin: 0 auto;
padding: 0;
}
#sideNav {
background-color: #96e6b3;
display: inline-block;
width: 20%;
position: fixed;
height: 100vh;
z-index: 2;
overflow: hidden;
-webkit-transform: translate(-290px);
/* Safari */
transform: translate(-290px);
*/
}
nav ul {
list-style-type: none;
font-family: Junction, "Times New Roman", sans-serif;
font-size: 1.9em;
margin-top: 24%;
}
nav ul li {
margin-top: 1%;
padding-top: 4.7%;
margin-left: -50px;
text-align: center;
}
nav a {
text-decoration: none;
color: #000000;
}
nav li:hover {
background-color: #34cf6d;
}
<!doctype html>
<html lang="en">
<head>
<title>Check</title>
<meta charset="UTF-8">
<link href="style.css" rel="stylesheet" type="text/css">
<link href="font.css" rel="stylesheet" type="text/css">
<script src="https://code.jquery.com/jquery-1.11.3.js"></script>
<link rel="stylesheet" href="styleCheck.css">
<style>
<!--very specific small CSS for this document--> #toggler {
text-decoration: underline;
}
#toggler:hover {
cursor: pointer;
}
</style>
</head>
<body>
<div>
<div class="o-content">
<div class="o-container">
<div class="o-grid">
<button id="checkButton" class="c-hamburger c-hamburger--htx" onclick="toggleNavBar()">
<span>toggle menu</span>
</button>
</div>
</div>
</div>
</div>
<div id="sideNav">
<nav>
<ul>
<li>Home
</li>
<li>Items
</li>
<li>XML
</li>
</ul>
</nav>
</div>
<!--side Nav-->
</body>
</html>
You should only declare your check variable once:
var check = "hidden";
function toggleNavBar() {
var nav = document.getElementById("sideNav"),
button = document.getElementById("checkButton");
if (check == "hidden") {
nav.style.transform = "translate(0px)";
check = "shown";
} else if (check == "shown") {
nav.style.transform = "translate(-290px)";
check = "hidden";
}
}
A few optimizations (assuming this is done after document is ready):
var toggleNavBar = (function() {
var check = "hidden";
var nav = document.getElementById("sideNav");
var button = document.getElementById("checkButton");
return function() {
if (check == "hidden") {
nav.style.transform = "translate(0px)";
check = "shown";
} else {
nav.style.transform = "translate(-290px)";
check = "hidden";
}
}
})();