function makeCategory() {
getList = document.getElementById("list");
cat = document.createElement("div");
cat.setAttribute("class", "cat");
getList.appendChild(cat);
cat.innerHTML =
'<input type="text" name="name"/><span class="removeButton" onclick= "remove(this)">-</span><br><span class="makeSubCat" onclick="makeSubCategory(this)">+Category</span>';
}
function remove(z) {
document.getElementById("list").removeChild(z.parentNode);
}
function makeSubCategory(i) {
y = document.createElement("input");
x = document.getElementsByClassName("cat");
x[i].appendChild(y);
}
Can't figure out how i can append child on each element of the class with "onclick". It's not working with "for loop" - it's always appends to the latest div. It's only works when i specify the number of class element.
Demo Outline
Reworked the HTML tags into a real Description List or <dl>
Instead of trying to manipulate multiple elements recursively, we will drive the functions by the click event
Use of Event Delegation saves us a ton of extra code. Only one eventListener() is needed for an unlimited number of <button>s and <input>s.
Template Literal was used in lieu of string literal
insertAdjacentHTML() played an important part of the add() function
Details are commented within the demo
Demo
// Reference the Description List dl#list
var dock = document.getElementById("list");
/* Callback function
|| if the clicked button is NOT dl#list...
|| - tgt/e.target is the clicked <button>
|| - if tgt has .add, then call add()...
|| else if tgt has .rem...
|| cat is its parent (.cat)
|| - Get div.cat parent and remove .cat
*/
function mod(e, dock) {
if (e.target !== e.currentTarget) {
var tgt = e.target;
if (tgt.className === 'add') {
add.call(this, dock);
} else if (tgt.className === 'rem') {
var cat = tgt.parentNode;
cat.parentNode.removeChild(cat);
} else return;
}
}
/* Register the ancestor of all of the .cat
|| and <button>s. (dl#list).
|| By doing this there's no need to addEventListeners
|| for every <button>
*/
dock.addEventListener('click', function(e) {
mod(e, this);
}, false);
/* This function expression takes a string (in this
|| case the string is a Template Literal.) and
|| parses it into HTML as it inserts it at a
|| position determined by the first parameter:
|| "beforeend"
|| (exactly like append)
*/
var add = function(dock) {
var cat = `<dd class='cat'>
<input name="name" type='text'>
<button class="rem">-</button>
<button class="add">+</button>
</dd>`;
dock.insertAdjacentHTML('beforeend', cat);
};
#list {
margin: 20px;
border: 2px ridge gold;
background: rgba(0, 0, 0, .3);
padding: 5px 10px 15px;
}
dt {
font: 700 20px/1 Consolas;
color: gold;
background: rgba(0, 0, 0, .5);
padding: 5px;
}
dd {
font-size: 16px;
color: #fff;
background: rgba(0, 0, 0, .5);
padding: 5px;
margin: 8px 4px 8px 0;
}
input,
button {
width: 10%;
font: inherit;
display: inline-block;
}
input[type='text'] {
width: 76%;
}
<dl id='list'>
<dt>Category List</dt>
<dd class='cat'>
<input name="name" type='text'>
<button class="add">+</button>
</dd>
</dl>
Related
I'm currently building a simple drag and drop Quiz but I'm allowed only to use html css and vanilla javascript.
The idea is there is a div with the answers and a div with the questions. In the questions text there are some blank divs where you can drop the draggable answers.
For example you have the answers "a,b,c" and "x,y,z" and the question is "The 1st three letters of the alphabet are: ___"
I need help on two main things:
I want to have the question's blank divs allow only one element drop per div. (I can stack them atm)
After drop I want to check if the answers in the current questions divs are correct.
How can I do?
P.S. I'm a newbie on html/css/js so maybe tell me just if it's not possible to impement this without external libraries and php.
/* Events fired on the drag target */
document.addEventListener("dragstart", function(event) {
// The dataTransfer.setData() method sets the data type and the value of the dragged data
event.dataTransfer.setData("Text", event.target.id);
// Output some text when starting to drag the p element
document.getElementById("demo").innerHTML = "Started to drag the p element.";
// Change the opacity of the draggable element
event.target.style.opacity = "0.4";
});
// While dragging the p element, change the color of the output text
document.addEventListener("drag", function(event) {
document.getElementById("demo").style.color = "red";
});
// Output some text when finished dragging the p element and reset the opacity
document.addEventListener("dragend", function(event) {
document.getElementById("demo").innerHTML = "Finished dragging the p element.";
event.target.style.opacity = "1";
});
/* Events fired on the drop target */
// When the draggable p element enters the droptarget, change the DIVS's border style
document.addEventListener("dragenter", function(event) {
if ( event.target.className == "droptarget" ) {
event.target.style.border = "3px dotted red";
}
});
// By default, data/elements cannot be dropped in other elements. To allow a drop, we must prevent the default handling of the element
document.addEventListener("dragover", function(event) {
event.preventDefault();
});
// When the draggable p element leaves the droptarget, reset the DIVS's border style
document.addEventListener("dragleave", function(event) {
if ( event.target.className == "droptarget" ) {
event.target.style.border = "";
}
});
/* On drop - Prevent the browser default handling of the data (default is open as link on drop)
Reset the color of the output text and DIV's border color
Get the dragged data with the dataTransfer.getData() method
The dragged data is the id of the dragged element ("drag1")
Append the dragged element into the drop element
*/
document.addEventListener("drop", function(event) {
event.preventDefault();
if ( event.target.className == "droptarget" ) {
document.getElementById("demo").style.color = "";
event.target.style.border = "hidden";
var data = event.dataTransfer.getData("Text");
event.target.appendChild(document.getElementById(data));
}
});
.droptarget {
display: inline-block;
min-width: 50px;
height: 25px;
border: 1px solid #aaaaaa;
color: #000;
text-align: center;
}
.container {
display: inline-block;
padding: 15px;
margin: 10px;
background: #eee;
border: 2px solid black;
border-radius: 5px;
box-sizing:border-box;
}
.dragtarget {
background-color: red;
padding: 5px;
border-radius: 5px;
color: #fff;
font-weight: bold;
text-align: center;
}
.domande {
display: inline-block;
padding: 15px;
margin: 10px;
background: #eee;
border: 2px solid black;
border-radius: 5px;
box-sizing:border-box;
}
<p>Trascina la risposta nel quadrato giusto</p>
<div class="container">
<p draggable="true" class="dragtarget" id="dragtarget">A,B,C</p>
<p draggable="true" class="dragtarget" id="dragtarget">1,2,3</p>
</div>
<div class="domande">
<h3>Prime tre lettere dell'alfabeto<div class="droptarget"></div></h3>
<h3>Primi tre numeri<div class="droptarget"></div></h3>
</div>
<p id="demo"></p>
Using the same id is really bad, it can only get the first element encounter with getElementById. Instead, I'll capture dragging DOM with dragstart and use it later on drop. In drop you just need to check if is there any child element inside of it. If it does, append that child back to .container.
You didn't include any details about how you will check so it's hard to help, I can only help you get the question and answer out.
var dragP;
/* Events fired on the drag target */
document.addEventListener("dragstart", function (event) {
// The dataTransfer.setData() method sets the data type and the value of the dragged data
// event.dataTransfer.setData("Text", event.target.id);
dragP = event.target;
// Output some text when starting to drag the p element
document.getElementById("demo").innerHTML = "Started to drag the p element.";
// Change the opacity of the draggable element
event.target.style.opacity = "0.4";
});
// While dragging the p element, change the color of the output text
document.addEventListener("drag", function (event) {
document.getElementById("demo").style.color = "red";
});
// Output some text when finished dragging the p element and reset the opacity
document.addEventListener("dragend", function (event) {
document.getElementById("demo").innerHTML = "Finished dragging the p element.";
event.target.style.opacity = "1";
});
/* Events fired on the drop target */
// When the draggable p element enters the droptarget, change the DIVS's border style
document.addEventListener("dragenter", function (event) {
if (event.target.className == "droptarget") {
event.target.style.border = "3px dotted red";
}
});
// By default, data/elements cannot be dropped in other elements. To allow a drop, we must prevent the default handling of the element
document.addEventListener("dragover", function (event) {
event.preventDefault();
});
// When the draggable p element leaves the droptarget, reset the DIVS's border style
document.addEventListener("dragleave", function (event) {
if (event.target.className == "droptarget") {
event.target.style.border = "";
}
});
/* On drop - Prevent the browser default handling of the data (default is open as link on drop)
Reset the color of the output text and DIV's border-color
Get the dragged data with the dataTransfer.getData() method
The dragged data is the id of the dragged element ("drag1")
Append the dragged element into the drop element
*/
document.addEventListener("drop", function (event) {
event.preventDefault();
let targetDiv = event.target;
if (targetDiv.className == "droptarget") {
document.getElementById("demo").style.color = "";
targetDiv.style.border = "hidden";
if (targetDiv.childElementCount != 0){
let childP = targetDiv.getElementsByTagName("p")[0];
document.getElementById("answer").appendChild(childP);
}
targetDiv.appendChild(dragP);
dragP = null;
}
});
document.getElementById("checkAnswer").addEventListener("click", function () {
let questions = document.getElementsByClassName("question");
let resultP = document.getElementById("result");
resultP.innerHTML = "";
for (let index = 0; index < questions.length; index++) {
const element = questions[index];
let childP = element.getElementsByTagName("p")[0];
let question = element.childNodes[0].textContent;
let answer = childP != undefined ? childP.innerText : "no answer";
resultP.append(`${question} : ${answer} ; `);
}
})
.droptarget {
display: inline-block;
min-width: 50px;
height: 25px;
border: 1px solid #aaaaaa;
color: #000;
text-align: center;
}
.container {
display: inline-block;
padding: 15px;
margin: 10px;
background: #eee;
border: 2px solid black;
border-radius: 5px;
box-sizing: border-box;
}
.dragtarget {
background-color: red;
padding: 5px;
border-radius: 5px;
color: #fff;
font-weight: bold;
text-align: center;
}
.domande {
display: inline-block;
padding: 15px;
margin: 10px;
background: #eee;
border: 2px solid black;
border-radius: 5px;
box-sizing: border-box;
}
<p>Trascina la risposta nel quadrato giusto</p>
<div class="container" id="answer">
<p draggable="true" class="dragtarget" id="dragtarget">A,B,C</p>
<p draggable="true" class="dragtarget" id="dragtarget">1,2,3</p>
</div>
<div class="domande">
<h3 class="question">Prime tre lettere dell'alfabeto<div class="droptarget"></div>
</h3>
<h3 class="question">Primi tre numeri<div class="droptarget"></div>
</h3>
</div>
<p id="demo"></p>
<button id="checkAnswer">Check</button>
<p id="result"></p>
This is code to render a form mixin in Node/Express (using Jade/Pug as the template engine)
My goal is to disable the button when there is no text in the textarea and enable the button when there is text persent. It works except the css classes (for visual styling) aren't getting applied.
My best guess is that I am changing the class of an object in memory (var btn) and not on the actual object in the DOM.
I can't work out how to do that. Can anyone help please?
mixin myForm()
form(action="/tweet/add" method="POST" class="formclassname")
textarea(
name='text'
id='textarea'
maxlength="300"
placeholder='What\'s happening?'
)
input(
type="submit"
id="button"
value="Send It"
class="button"
disabled = true
)
script.
var btn = document.getElementById('button');
var inpt = document.getElementById('textarea');
inpt.addEventListener("input", function(){
if (this.value != '') {
btn.disabled = false;
btn.class = 'butOn';
console.log('button enabled');
}
else {
btn.class = 'butOn';
console.log('button disabled');
}
})
CSS
.button{
/* Style Font */
text-align: center;
text-decoration: none;
font-weight: bold;
color: rgb(255, 255, 255);
/* Style "button" */
background-color: rgba(0, 166, 255);
border-radius: 25px;
border: 2px solid rgb(0, 166, 255, 0.6);
/* Position the button */
padding: 10px 20px 10px 20px;
margin: 5px 10px 5px 10px;
}
.butOn {
background-color: #1dec0a;
}
.butOff {
background-color: rgb(147, 208, 249);
}
Typical! I spend 30 minutes typing the question then find an answer 5 seconds later.
Leaving this here to help others.
How can I change an element's class with JavaScript?
TLDR... classList.remove and .add as follows
script.
var btn = document.getElementById('button');
var inpt = document.getElementById('textarea');
inpt.addEventListener("input", function(){
if (this.value != '') {
btn.disabled = false;
btn.classList.remove('butOff');
console.log(btn);
}
else {
btn.classList.add('butOff');
btn.disabled = true;
console.log('no text present');
}
})
I am trying to make use of createElement, createTextNode and appendChild to rewrite an outdated simple to-do list code example.
The code example requires the use of the array join() method so, unfortunately, this can't be removed. The old version just wrote the HTML for the ul list in code fragments.
I am unsure how to proceed where I have entered to comment at line 30 of the js: " need to render the tasks (from stringToInsert) as a list to div id="output" here "
I have referred to the following stackoverflow articles to help me rewrite the code:
js-how-to-concatenate-variables-inside-appendchild -This example uses join() and appendChild but not list items.
create-ul-and-li-elements-in-javascript-
From that one, I copied the code from a Fiddle and put it into function createul() in my example codepen
Once I have the function addTask() working createul() and it's associated HTML elements (such as the render list button) will be removed.
// tasks.js #2
// This script manages a to-do list.
// Need a global variable:
var tasks = [];
function addTask() {
'use strict';
console.log("addTask started");
// Get the task:
var task = document.getElementById('task');
// Reference to where the output goes:
var output = document.getElementById('output');
if (task.value) {
tasks.push(task.value);
// Update the page:
//var message = '<h2>To-Do</h2>';
var stringToInsert = tasks.join(' : ');
console.log(stringToInsert);
var taskUl = document.createElement('ul');
taskUl.setAttribute('id', 'autoTask');
document.getElementById('output').appendChild(taskUl);
/* need to render the tasks (from stringToInsert) as a list to div id ="output" here */
document.getElementById("task").value = '';
}
// Return false to prevent submission:
return false;
} // End of addTask() function.
function createul() {
var ul = document.createElement('ul');
ul.setAttribute('id', 'proList');
var t, tt;
productList = ['Electronics Watch', 'House wear Items', 'Kids wear', 'Women Fashion'];
document.getElementById('renderList').appendChild(ul);
productList.forEach(renderProductList);
function renderProductList(element, index, arr) {
var li = document.createElement('li');
li.setAttribute('class', 'item');
ul.appendChild(li);
t = document.createTextNode(element);
li.innerHTML = li.innerHTML + element;
}
}
function init() {
document.getElementById('renderbtn').addEventListener("click", createul);
document.getElementById('theForm').onsubmit = addTask;
}
window.addEventListener('load', init);
/* css (I have simplified this a little for this example and I am sorry I haven't cut it down further) */
form {
margin: 0 auto;
width: 400px;
padding: 14px;
background-color: #ffffff;
border: solid 2px #425955;
}
/* ----------- stylized ----------- */
h1 {
font-size: 14px;
font-weight: bold;
margin-bottom: 8px;
}
p {
font-size: 11px;
color: #666666;
margin-bottom: 20px;
border-bottom: solid 1px #BFBD9F;
padding-bottom: 10px;
}
label {
display: block;
font-weight: bold;
text-align: right;
width: 140px;
float: left;
}
select {
float: left;
font-size: 12px;
padding: 4px 2px;
border: solid 1px #BFBD9F;
width: 200px;
margin: 2px 0 20px 10px;
}
input {
float: left;
font-size: 12px;
padding: 4px 2px;
border: solid 1px #BFBD9F;
width: 200px;
margin: 2px 0 20px 10px;
}
#submit {
clear: both;
margin-left: 150px;
width: 125px;
height: 31px;
background: #F1F2D8;
text-align: center;
line-height: 20px;
color: #000000;
font-size: 12px;
font-weight: bold;
}
#output {
clear: both;
margin-bottom: 10px;
color: blue;
}
<form action="#" method="post" id="theForm">
<div><label for="task">Task</label><input type="text" name="task" id="task" required></div>
<input type="submit" value="Add It!" id="submit"><br>
<button type="button" id="renderbtn">render list</button>
<div id="renderList"></div>
<div id="output"></div>
edit: I can just convert it back to an array with something like the following if there is no other way of doing it.
var ar = stringToInsert.split(' : ');
or something based on:
stringToInsert.split(' : ').forEach ... or if that doesn't work I could try map()
I'm going to show you a different approach that may help clear things up -
function ul (nodes)
{ const e = document.createElement("ul")
for (const n of nodes)
e.appendChild(n)
return e
}
function li (text)
{ const e = document.createElement("li")
e.textContent = text
return e
}
function onSubmit (event)
{ event.preventDefault()
tasks.push(f.taskInput.value)
f.taskInput.value = ""
render()
}
function render ()
{ const newList = ul(tasks.map(li))
f.firstChild.replaceWith(newList)
}
const tasks = [ "wash dishes", "sweep floors" ] // <- initial tasks
const f = document.forms.main // <- html form
f.addButton.addEventListener("click", onSubmit) // <- button listener
render() // <- first render
<h3>todo list</h3>
<form id="main">
<ul></ul>
<input name="taskInput" placeholder="example: paint fence">
<button name="addButton">Add Task</button>
</form>
And here's a more modern approach using a DOM library like React -
const { useState, useRef } = React
const { render } = ReactDOM
function TodoList ({ initTasks = [] })
{ const [ tasks, updateTasks ] =
useState(initTasks)
const inputEl =
useRef(null)
function onSubmit () {
updateTasks([ ...tasks, inputEl.current.value ])
inputEl.current.value = ""
}
return <div>
<h3>todo list</h3>
<ul>{tasks.map(t => <li children={t} />)}</ul>
<input ref={inputEl} placeholder="ex: paint fence" />
<button onClick={onSubmit}>add</button>
</div>
}
render
( <TodoList initTasks={[ "wash dishes", "sweep floors" ]}/>
, document.body
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
I'm learning how to code javascript, please do not reply that this can be done by using someone else's code that already exists. I am doing this to learn.
I have a situation whereby the below CSS and HTML code:
CSS:
div.miniblock {
font-size: 12px;
background-color: #333333;
text-align: center;
vertical-align: middle;
border: thin dotted #CCCCCC;
color: #FFFFFF;
padding: 2px;
margin: 5px;
cursor: move;
position: relative;
}
div.miniblock_unsaved {
font-size: 12px;
background-color: #55AAAA;
text-align: center;
vertical-align: middle;
border: thin dotted #000;
color: #000;
padding: 2px;
margin: 5px;
cursor: move;
position: relative;
}
div.dropinto {
font-size: 12px;
background-color: #575757;
text-align: center;
vertical-align: middle;
border: thin dotted #AAAAAA;
color: #FFFFFF;
padding: 2px;
margin: 5px;
}
div.dropinto_over {
font-size: 12px;
background-color: #FFFFFF;
text-align: center;
vertical-align: middle;
border: thin dotted #000000;
color: #000000;
padding: 2px;
margin: 5px;
}
div.moving {
font-size: 12px;
background-color: #CCCCCC;
text-align: center;
vertical-align: middle;
border: thin dotted #000000;
color: #000000;
z-index:1;
height: 80px;
width: 80px;
opacity: 0.7;
padding: 5px;
cursor: move;
}
div.OPAQUE {
font-size: 12px;
background-color: #FFF;
text-align: center;
vertical-align: middle;
color: #000000;
opacity: 0.5;
}
HTML:
<INPUT TYPE="HIDDEN" NAME="block_side[3]" VALUE="" ID="block_side0">
<INPUT TYPE="HIDDEN" NAME="block_order[3]" VALUE="" ID="block_order0">
<INPUT TYPE="HIDDEN" NAME="block_side[4]" VALUE="" ID="block_side1">
<INPUT TYPE="HIDDEN" NAME="block_order[4]" VALUE="" ID="block_order1">
<INPUT TYPE="HIDDEN" NAME="block_side[5]" VALUE="" ID="block_side2">
<INPUT TYPE="HIDDEN" NAME="block_order[5]" VALUE="" ID="block_order2">
<INPUT TYPE="HIDDEN" NAME="block_side[6]" VALUE="" ID="block_side3">
<INPUT TYPE="HIDDEN" NAME="block_order[6]" VALUE="" ID="block_order3">
<INPUT TYPE="HIDDEN" NAME="block_side[2]" VALUE="L" ID="block_side=4">
<INPUT TYPE="HIDDEN" NAME="block_order[2]" VALUE="1" ID="block_order4">
<INPUT TYPE="HIDDEN" NAME="block_side[1]" VALUE="L" ID="block_side=5">
<INPUT TYPE="HIDDEN" NAME="block_order[1]" VALUE="2" ID="block_order5">
<DIV CLASS="OPAQUE" ID="instruct"></DIV>Set your preferences for the blocks of information and their display location here.<TABLE height="100%" width="100%"><TR ><TH COLSPAN="2">Assigned Blocks</TH></TR><TR ><TD COLSPAN="2">Here are the blocks that are currently displayed during your experience.</TD></TR><TR ><TD WIDTH="50%" VALIGN="TOP"><DIV CLASS="dropinto" ID="leftblocks" SLOT="L">Left Blocks<div onmousedown="grabobj(4)" onmousemove="dragobj(4)" onmouseup="dropobj(4)" id="4" name="2" class="miniblock">Quick Links [2]</div><div onmousedown="grabobj(5)" onmousemove="dragobj(5)" onmouseup="dropobj(5)" id="5" name="1" class="miniblock">Session Information [1]</div></DIV></TD><TD WIDTH="50%" VALIGN="TOP"><DIV CLASS="dropinto" ID="rightblocks" SLOT="L">Right Blocks</DIV></TD></TR><TR ><TH COLSPAN="2">Unassigned Blocks</TH></TR><TR ><TD COLSPAN="2" ID="unassigned_blocks">Here are the blocks that are not currently displayed during your experience.<div onmousedown="grabobj(0)" onmousemove="dragobj(0)" onmouseup="dropobj(0)" id="0" name="3" class="miniblock">Another Block [3]</div><div onmousedown="grabobj(1)" onmousemove="dragobj(1)" onmouseup="dropobj(1)" id="1" name="4" class="miniblock">And Another [4]</div><div onmousedown="grabobj(2)" onmousemove="dragobj(2)" onmouseup="dropobj(2)" id="2" name="5" class="miniblock">Poll Voting [5]</div><div onmousedown="grabobj(3)" onmousemove="dragobj(3)" onmouseup="dropobj(3)" id="3" name="6" class="miniblock">SysAdmin Block [6]</div></TD></TR></TABLE>
and the below Javascript:
window.instruct = function(id, instr){
var el = document.getElementById(id);
el.innerHTML = instr;
}
window.blockprefs = function(id, thisblock){
// determine which slot thisblock belongs to
s = id.getAttribute('SLOT');
// identify all the child nodes associated to this slot
c = id.childNodes;
for(var i=1;i < c.length; i++) { // I set i = 1 here because I don't care about the parent element at this time.
name = c[i].getAttribute('NAME');
// console.log(c.length,c[i]);
pos = document.getElementById('block_side'+name);
ord = document.getElementById('block_order'+name);
pos.setAttribute('VALUE',s);
ord.setAttribute('VALUE',i);
console.log(name,pos,ord,c[i]);
}
};
window.grabobj = function(id){
// console.log('moving object: '+id);
// console.log(document.getElementById(id));
// console.log(event.clientX+', '+event.clientY);
thisblock = document.getElementById(id);
thisblock.setAttribute('CLASS','moving');
thisblock.setAttribute('STATUS','click');
thisblock.style.position = 'absolute';
thisblock.style.left = event.pageX - 40;
thisblock.style.top = event.pageY - 20;
// id.addEventListener('mousemove',function(){console.log('moving mouse: x('+event.clientX)+') y('+event.clientY+')';},false);
};
window.drop = function(id, hit) {
id.setAttribute('STATUS','drop');
id.setAttribute('CLASS','miniblock_unsaved');
id.setAttribute('STYLE','');
instruct('instruct','You have unsaved changes, be sure to commit your changes below.');
};
window.dropobj = function(id){
thisblock = document.getElementById(id);
if(thisblock.getAttribute('STATUS') == 'drag' || thisblock.getAttribute('STATUS') == 'click'){
// determine if the block was dropped within one of the drop object targets
// if it was not, return it to the original location, otherwise, drop it into the new location
var left = document.getElementById("leftblocks"),
right = document.getElementById('rightblocks'),
leftbounds = left.getBoundingClientRect(),
rightbounds = right.getBoundingClientRect(),
t = window.pageYOffset || document.documentElement.scrollTop,
l = window.pageXOffset || document.documentElement.scrollLeft;
if(event.clientX >= leftbounds.left && event.clientX <= leftbounds.right && event.clientY >= leftbounds.top && event.clientY <= leftbounds.bottom){
// hit on the left blocks bounds, drop it into the left block
left.appendChild(thisblock);
//thisblock.insertBefore(left.firstchild);
drop(thisblock);
// now assign the hidden form element the correct values based on the current config in the left block then
// here is what I think will have to happen:
// identify all child nodes of the parent node
// identify all of the hidden form fields associated to those child nodes
// update all of the hidden form fields associated to those child nodes with
// the correct order and L / R flag.
// not sure how to complete those tasks at this time.
// console.log( document.getElementById('block_order' + thisblock.getAttribute('ID')));
// console.log( left.childElementCount );
blockprefs(left, thisblock);
} else if(event.clientX >= rightbounds.left && event.clientX <= rightbounds.right && event.clientY >= rightbounds.top && event.clientY <= rightbounds.bottom){
// hit on the right blocks bounds, drop it into the right block
right.appendChild(thisblock);
//thisblock.insertBefore(right.firstchild);
drop(thisblock);
// now assign the hidden form element the correct values based on the current config in the left block then
} else {
// user missed dropping into the left or right box, drop it back into unassigned.
var u = document.getElementById("unassigned_blocks");
u.appendChild(thisblock);
drop(thisblock);
}
}
};
window.dragobj = function(id){
thisblock = document.getElementById(id);
if(thisblock.getAttribute('STATUS') == 'click' || thisblock.getAttribute('STATUS') == 'drag'){
thisblock.style.left = event.pageX - 40;
thisblock.style.top = event.pageY - 20;
thisblock.setAttribute('STATUS','drag');
}
};
window.onload = function() {
// on mouseover change color of leftdiv or right div
var left = document.getElementById("leftblocks");
var right = document.getElementById('rightblocks');
console.log(left.nodeValue);
function block_over(block){ // set the attribute of the block on mouse over
// console.log('setting property of block: '+block);
block.setAttribute('CLASS','dropinto_over');
}
function block_out(block){ // set the attribute of the block on mouse out
block.setAttribute('CLASS','dropinto');
}
left.addEventListener('mouseover',function(){block_over(left); },true); // listener to set the left block's attributes
left.addEventListener('mouseout',function(){block_out(left); },true);
right.addEventListener('mouseover',function(){block_over(right); },true); // listener to set the right block's attributes
right.addEventListener('mouseout',function(){block_out(right); },true);
};
I attempted to put this into a JSFIDDLE https://jsfiddle.net/vt1hcLL6/ but the frames were throwing it off so it might have to just go into a flat html file sorry.
As a user, the intent of this code is to be able to pick up the blocks and move them into the slots (either the left or the right side) blocks, or remove them from those blocks.
After doing so, javascript will set some values in the hidden form fields so that upon saving the page php will grab those values.
Currently it's only setup to do so if you move a block into the left side and yes once I get this all nailed from a hardcoded perspective I will abstract it further, for now some of this is hard coded.
My problem is that upon the first instance of dropping a block into the Left Side Block, everything works fine all the HIDDEN form fields update correctly with the SLOT="L" Attribute and the ORDER="i" attribute (i being the number corresponding to the child iteration.
Great! It works! Now... once a second block is added to that set from down below, the code breaks and I can't figure out why.
The code that is performing this functionality is in the blockprefs( ) function where I iterate through all the child nodes of the Left block and attempt to bring in the hidden form elements belonging to each child. I get a:
divblock.js:33 Uncaught TypeError: Cannot read property 'setAttribute' of null
I am experimenting with a script from another stackoverflow post. I have a textarea element that someone can enters text. By double clicking on it, it converts to div and it keeps line breaks. When you dblclick on the div to turn back to textarea, the <br />s are left as are.
You can see the jsfiddle right here http://jsfiddle.net/QUFZJ/
How can I get back the text without the br's but keeping the line breaks ?
You should use
boxText = $(this).html().replace(/<br\s?\/?>/g,"\n");
instead of
$(this).val().replace(/<br\s?\/?>/g,"\n");
You need to assign new value to boxText because You set text area value like this
$(this).replaceWith( '<textarea form="HTML" class="BoxText">' + boxText + '</textarea>' );
jsFiddle example
If you're willing to experiment, I think you'd probably have a much more satisfying time working with contenteditable elements. Here's a short demo I worked up to show you how it could work.
You'll probably notice that you don't see one there in the HTML section. Look down in the $.ready() block and you'll see I actually switch between two different divs, as (AFAICT) you can't change a contenteditable once it's been added to the DOM. So my solution was to switch out what I needed. Someone let me know know if there is a way to do that, too, in case I'm overlooking something.
Note, this is not complete and while it works (seemingly) in most browsers, Opera I know has a problem with something, and I haven't really thrown that much variable text at it. It seems to work best in Chrome and Firefox. There's definitely some hamfisting going on trying to interpret pasted in markup and smooth over the different ways that each major browser interprets when you interact with a contenteditable.
It's a start. Check it out, see what you think.
HTML
<div class="boxtext" class="editable">Text Text Text...</div>
CSS
body, html {
margin: 5px;
padding: 15px 5px 5px 0;
}
.boxtext,
.boxtext .editable,
.boxtext .uneditable,
.boxtext pre.text {
display: block;
width: 500px;
padding: 5px;
margin: 0 auto;
}
.boxtext .uneditable {
background: #ddd;
border: 1px solid #ccc;
}
.boxtext .editable,
.boxtext .uneditable {
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.boxtext .editable {
border: 1px solid #5794BF;
border-right-color: #C3D4E0;
border-bottom-color: #C3D4E0;
}
.boxtext pre.text {
white-space: pre-wrap;
margin-top: -3px;
background: #444;
border: 1px solid black;
color: white;
font-family: monospace;
-webkit-border-radius: 0 0 3px 3px;
-moz-border-radius: 0 0 3px 3px;
border-radius: 0 0 3px 3px;
}
h1, h2, h3, h4 {
font-weight: bold;
}
Javascript
(function ready($, win, doc, _c, _l, copies) {
var $editable = $('<div class="editable" contentEditable="true">'),
$uneditable = $('<div class="uneditable">'),
$div = $('<div>'),
$pre = $('<pre class="text">'),
$doc = $(doc),
$body,
$boxes;
$doc.ready(setup);
function setup() {
$body = $('body', doc);
$boxes = $('.boxtext');
$boxes.wrapInner($div.clone()).wrapInner($uneditable.clone());
while (copies--) {
$body.append($boxes.clone());
}
$boxes = $(".boxtext");
$doc.on('click', $body, not);
$boxes
.on('dblclick.editable', '.editable, .uneditable', edit)
.on('paste.editable', '.editable', paste);
}
function not(e) {
!!$boxes.has(e.target).length || close.call(doc, e, true);
}
function close(e, call) {
if (call) {
$boxes.find('.editable').not(this).trigger('dblclick.editable');
}
}
function edit(e) {
var $this = $(this),
$box = $boxes.has($this),
$shim = $uneditable,
type = '.uneditable';
close.call(this, e, true);
if ($this.is(type)) {
$shim = $editable;
type = '.editable';
}
$shim = $this.wrapInner($shim.clone()).find(type);
$box.empty().append($shim);
if (type == '.uneditable') {
text.call($box[0]);
}
}
function paste(e) {
var $this = $(this),
$target = $(e.target);
(function a(th, ev) {
function f(){clean.call(th, ev);}
setTimeout(f, 1);
})(this, e);
}
function clean(e) {
var $this = $(this),
$pres = [];
$this.find('div > p').not(':empty').unwrap();
$this.find(':empty').remove();
$this.find('pre').each(function r(i, el) {
$pres[i] = $(el).html();
});
$this.find('*')
.not('h1, h2, h3, h4, p, div, br, pre, code')
.children().unwrap();
$this.html($.trim($this.html().replace(/(\r\n|\n|\r)/gm, ' ')));
$this.html($.trim($this.html().replace(/>[ ]{1,}</gm, '><')));
$this.find('pre').each(function r(i, el) {
$(el).html($pres[i]);
});
$this.find('h1, h2, h3, h4, div, p, pre').after('<br/>');
$this.find('br:last').remove();
}
function text(e) {
var $this = $(this),
$uneditable = $this.find('.uneditable').clone(),
$text = $pre.clone();
$uneditable.find('div > br').unwrap();
$uneditable.find('div, p, br').after('\n');
$text.text('Plaintext\n---------\n' + $uneditable.text());
$this.append($text);
}
function log() {
!_c || !_l.apply || _l.apply(_c, arguments);
}
})(jQuery, window, document, console, console.log, 5);
http://jsfiddle.net/userdude/76agk/