textarea where part of the text can be moved but NOT deleted - javascript

Above is generally what I'm trying to create. There has to be a piece of text which is always in the textarea. It can be dragged to a different part of the text area, but it can never be deleted. You can type both before it and after it.
I've been trying to find the best approach to accomplish this for several days and have come up short. Anyone ideas?

A start , using contentEditable parent element , div having input type="text" element with disabled attribute set to "true" . TODO: Drag and Drop adjustments ; currently only utilizes .appendChild to drop div containing input to parent div , without placing draggable element between editable text in parent contentEditable div
input {
color: dodgerblue;
font-size: 28px;
font-weight: bold;
font-family: arial;
background: none;
border: none;
width: 220px;
padding: 0px;
margin: 0px;
position: relative;
display: inline-block;
}
div[droppable="true"] {
width: 450px;
height: 300px;
border: 1px solid #000;
}
div[draggable="true"] {
width: 250px;
}
div[draggable="true"]:hover {
cursor: pointer;
}
<script>
function dragstartHandler(e) {
// add `input` element's name to the data transfer object
e.dataTransfer.setData("text/plain", e.target.firstElementChild.name);
}
function dropHandler(e) {
e.preventDefault();
// get the `name` of the target and add the moved element to the target's DOM
var data = e.dataTransfer.getData("Text");
e.target.appendChild(
document.querySelector("input[name=" + data + "]").parentElement
);
}
document.ondragover = function(event) {
// prevent default to allow drop
// TODO: drop `input` parent `div` element between `contentEditable` text
event.preventDefault();
console.log(event)
};
</script>
<div contentEditable="true" droppable="true" ondrop="dropHandler(event);">
This text can be edited
<div draggable="true" ondragstart="dragstartHandler(event);">
<input disabled name="fixed" type="text"
minlength="14" maxlength="14" value="BUT THIS CAN'T" />
</div>and so can this one here
</div>

Related

How to paste multiline text in a contenteditable element so that each line is always in its own div?

Run the code and copy and paste any two lines of plain text into the box. It will paste the way I want, each line in its own div.
Now delete everything and press enter to create an empty div, then paste the text into that div. The unwanted behavior will be seen.
There's more examples of inconsistent behavior.
For example, delete everything and paste the text again. Now, select all and paste over the selection. It will behave like I want. Now, press enter to create a new div below the two existing ones, select all the text and paste it again. The unwanted behavior will be seen.
Some of the issues seem to do with the break tags generated. I have tried to work around it but one fix breaks something else.
.text_area {
width: 200px;
height: 100px;
border: 1px solid;
padding: 2em 2em 2em 3em;
overflow: auto;
}
.text_area>div {
border: 1px solid lightgray;
margin: 0.33em 0;
min-height: 1.2em;
}
<div class="text_area" contenteditable="true"></div>
If you always want each line to be in a div, you have to ensure that there is always a div for the content to go in.
Initialize your contenteditable element with an inner div to start. That ensures that when the user clicks in they'll already be in the correct environment.
Using JavaScript with an event listener on 'input' you can test if the contenteditable element becomes empty. You have to remember that <br> in the area would count as non-empty. This is what's causing your issues with enter. There's a <br> before the <div>s. If the innerHTML of your contenteditable area, excluding <br> and excess whitespace, then you need to append that div back as the child of the contenteditable area.
Lastly, you were getting div nesting when you were copying and pasting. That is because the default behavior of paste in a contenteditable area is to use HTML formatting. So if you copy <div>a</div><div>b</div> and paste it into <div></div> you'll get nesting. You can avoid this by getting only 'text/plain' from your clipboard instead.
// Ensure Text is copied as plain text not as HTML (Fixes Nested Divs Problem)
document.querySelector('[contenteditable]').addEventListener('paste', (event) => {
event.preventDefault();
document.execCommand('inserttext', false, event.clipboardData.getData('text/plain'));
});
document.querySelector('[contenteditable]').addEventListener('input', (event) => {
// Remove All BR
if (event.target.childNodes.length > 0) {
for (let br of event.target.querySelectorAll('br')) {
br.remove();
}
}
// If DIV is ever made empty after removal of BRs
if (event.target.innerHTML.trim().length === 0) {
// Re-populate the initial DIV
event.target.innerHTML = '';
event.target.appendChild(document.createElement('div'));
}
});
.text_area {
width: 200px;
height: 100px;
border: 1px solid;
padding: 2em 2em 2em 3em;
overflow: auto;
}
.text_area>div {
border: 1px solid lightgray;
margin: 0.33em 0;
min-height: 1.2em;
}
<!-- Prime Your Content Editable Area With a DIV -->
<div class="text_area" contenteditable="true">
<div></div>
</div>

Input field is not focusing when clicked

I have an <input> field inside a div that is giving me trouble.
It is inside a div with position absolute. When I click on it, it does not get the focus, so I cannot type inside it.
The other parts of an input field work as they should: The cursor changes to the text symbol when over it, I can focus on it using the right-click with the mouse or the Tab key and when it DOES get focus I can type on it normally.
I even binded a console log to it when clicked, just to make sure the the correct element being clicked. The log does happen, but it still doesn't get the focus on clicking.
Does anyone have an idea of what may be happening here?
Edit: added more parts of my code, sorry for having such little code before.
Here is my code:
// link that makes the form appear, on another part of the UI
jQuery("#link").on("click", function() {
jQuery(".form").show()
})
jQuery("#close-button").on("click", function() {
jQuery(".form").hide()
})
// This was added to test if the click was happening,
// it does not work with or without this
jQuery("#input-field").on("click", function(e) {
console.log("clicked")
console.log(e.target) // this is returning the "input-field" element
})
.form {
background-color: #EAE8E8;
position: absolute;
width: 99%;
right: 0;
z-index: 100;
bottom: 0;
display: none;
border: 1px solid;
}
#close-button {
float: right;
cursor: pointer;
}
/* input-field doesn't have any CSS defined by code yet */
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="link">Click to show form</button>
<div class="form">
<!-- this has position: absolute -->
<img src="'/close.png" id="close-button">
<!-- Here are some other images that can be clicked... that all works fine -->
<input id="input-field" />
<!-- this is not getting focused when clicked -->
</div>
You might add .focus() to autofocus your desired input. Here is your example:
jQuery("#link").on("click", function() {
jQuery(".form").show()
// Add to auto focus your input
jQuery("#input-field").focus();
})
jQuery("#close-button").on("click", function() {
jQuery(".form").hide()
})
.form {
background-color: #EAE8E8;
position: absolute;
width: 99%;
right: 0;
z-index: 100;
bottom: 0;
display: none;
border: 1px solid;
}
#close-button {
float: right;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="link">Click to show form</button>
<div class="form">
<img src="'/close.png" id="close-button">
<input id="input-field" />
</div>

Make popup have smart positioning

I am working on a piece of legacy code for a table. In certain cells, I'm adding a notice icon. When you hover over the icon a <span> is made visible displaying some information. I would like to be able to make this <span> smart about its positioning but can't figure out a good method. I can statically position it but depending on which cell in the table it is in it gets lost against the edge of the page. I have done a JsFiddle here demonstrating the issue. Unfortunately, I am not allowed to use anything but HTML, CSS and vanilla JS.
The title attribute to most tags is pretty smart about its position. I have added a title to one of the cells in the table in the jsFiddle (cell containing "Hello"). Is there any way to make my span exhibit the same smart behaviour?
A pop-up can be added before any element by putting the popup html code inside a 'div' with 'position:absolute; overflow:visible; width:0; height:0'.
When these events: 'onmouseenter', 'onmouseleave' are fired on the element, just toggle the popup css attribute 'display' between 'none' and 'block' of the element.
Example on jsfiddle:
https://jsfiddle.net/johnlowvale/mfLhw266/
HTML and JS:
<div class="popup-holder">
<div class="popup" id="popup-box">Some content</div>
</div>
Some link
<script>
function show_popup() {
var e = $("#popup-box");
e.css("display", "block");
}
function hide_popup() {
var e = $("#popup-box");
e.css("display", "none");
}
</script>
CSS:
.popup-holder {
position: absolute;
overflow: visible;
width: 0;
height: 0;
}
.popup {
background-color: white;
border: 1px solid black;
padding: 10px;
border-radius: 10px;
position: relative;
top: 20px;
width: 300px;
display: none;
}

Identical elements have different scrollHeights when overflowed. What is the cause, and how can I fix it?

I have a plugin that creates and, on an interval, populates a <p> with the content of a <textarea>. The plugin positions the <p> underneath the <textarea>, and styles the elements so that their "boxes" are identical. Additionally, the background and text of the <textarea> are defined as transparent so that the content of the <p> can be seen.
Ideally, the elements and their contents will mirror one another at all times. And in most cases, they do. However, when both elements are made to be scrollable, the dynamic breaks; this is due to a difference in the scrollHeight of the two elements (the scrollHeight of <textArea> is larger than that of the <p>)
Here is the code:
var $shadowParagraphObj = $("#shadowParagraph");
var $contentTextAreaObj = $("#contentTextArea").scroll(scrollShadowParagraph);
function scrollShadowParagraph(event)
{
var textAreaScrollLeft = $contentTextAreaObj.scrollLeft();
var textAreaScrollTop = $contentTextAreaObj.scrollTop();
if($shadowParagraphObj.scrollLeft() != textAreaScrollLeft)
$shadowParagraphObj.scrollLeft(textAreaScrollLeft)
if($shadowParagraphObj.scrollTop() != textAreaScrollTop)
$shadowParagraphObj.scrollTop(textAreaScrollTop)
}
var intervalId = setInterval(function(){$shadowParagraphObj.html($contentTextAreaObj.val())}, 100);
#containerDiv {
position: relative;
left: 50%;
margin-left: -250px;
width: 510px;
height: 200px;
}
#shadowParagraph, #contentTextArea {
width: inherit;
height: inherit;
overflow: scroll !important;
padding: 4px;
border : none;
outline: none;
margin: 0px;
white-space: pre-wrap;
word-wrap: pre-wrap;
font: 1em Arial, sans-serif;
}
#shadowParagraph {
position: absolute;
z-index: 0;
background: white;
color: blue;
}
#contentTextArea {
position: relative;
z-index: 1;
background: transparent;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='containerDiv'>
<p id='shadowParagraph'></p>
<textarea id='contentTextArea'></textarea>
</div>
Overflowing the <textarea> should produce the issue (the text of the <textarea> has been given color to make the issue easy to see).
Have I forgot to declare some properties that are causing this discrepancy between scrollHeight values? If so, what are they and how should I declare them? If not, is there any way to ensure that the scrollHeight of the two elements is equal at all times?
Okay so using .replace(/\n\r?g, '<br />') to convert updated values, your line breaks will be converted into html line breaks. Additionally, html tends to ignore lone <br /> line breaks, so you will want to add an additional <br /> to the value to ensure the last line break is rendered.
Put together this would look something like:
var textAreaHTML = $myTextArea.val().replace(/\n\r?g, '<br />')+'<br />';
Additionally, I would recommend updating your textarea values AND scroll position on the .keyup() event, .keypress() event, or both events using .on('keyup keypress', function() {...}).
To see this in action check out this jsFiddle example

HTML5 Drag & Drop and touching text

I have a weird issue with drag & drop in HTML5.
There are 3 target zones which are divs with text in them and 1 source zone which is a div containing a image.
I use dragenter and dragleave events to change the border of the active target zone to project where the dragged object is going to land.
The problem is that as soon as you drag it over text, it for some reason fires the dragleave event, removing the border.
Here is a jsfiddle example illustrating the problem
And here is some inline code:
HTML
<h1>Targets</h1>
<div class="targets">
<div class="target">I am a target<br/>Touch text while dragging to see the problem</div>
<div class="target">I am a target<br/>Touch text while dragging to see the problem</div>
<div class="target">I am a target<br/>Touch text while dragging to see the problem</div>
</div>
<h1>Source</h1>
<div class="source" draggable="true">
<img class="source_image" src="http://lorempixel.com/output/technics-q-c-184-69-8.jpg" alt="image" width="184" height="69"/>
</div>
JS
$("div.source").on('dragstart', function(e) {
$(this).fadeTo('slow', 0.4);
});
$("div.source").on('dragend', function(e) {
$(this).fadeTo('slow', 1);
});
$("div.target").on('dragover', function(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'move';
return false;
});
$("div.target").on('dragenter', function(e) {
$(this).addClass('over');
});
$("div.target").on('dragleave', function(e) {
$(this).removeClass('over');
});
CSS
h1 {
font-size: 2em;
text-align: center;
}
.target {
margin: 1em;
display:inline-block;
width: 184px;
height: 69px;
border: 5px #995555 solid;
border-radius: 5px;
background-color: #AAAAAA;
vertical-align: bottom;
text-align: center;
font-size: 1.1em;
font-weight: bold;
line-height: 0.9em;
}
.target.over {
border: 5px #0A0 dashed;
}
.source {
margin: 1em;
display:inline-block;
width: 184px;
height: 69px;
border: 5px #555599 solid;
border-radius: 5px;
background-color: #CCCCCC;
vertical-align: bottom;
text-align: center;
font-size: 1.4em;
font-weight: bold;
}
Does anyone know of any solutions to keep the border changed even when touching text?
Another question would be whether it is possible to keep the border around the source div being dragged?
On a final note, I realize that both of these things can be done by using jQuery UI draggable and droppable, but I'm specifically wondering if it is possible to do this with native HTML5 drag & drop.
The problem is that the text constitutes an extra node in the DOM, and dragleave and dragenter take into account child elements as well as parent elements. When the cursor enters the text node, it leaves the div. This is similar to the mouseout vs mouseleave issue. A simple way to work around it is to keep a count of the events and only remove the style when all are accounted for:
var count=0;
$("div.target").on('dragenter', function(e) {
$(this).addClass('over');
count++;
}).on('dragleave', function(e) {
if (--count<=0) {
$(this).removeClass('over');
}
});
This isn't necessarily completely reliable (remember to set count to zero in the drop event), but it'll work better than your current setup. Another option is to not put any content in the div elements at all, instead add it with CSS:
.target:after {
content: 'I am a target \A Touch text while dragging to see the problem';
white-space: pre-wrap;
}
This has some accessibility drawbacks, because generated content is invisible to assistive technology, and will only let you add text, but is in many ways a cleaner solution.

Categories

Resources