drag and drop to contenteditable element second and subsequent lines - javascript

I want to drag and drop the tag to the contenteditable="true" element string using html5 and javascript.
<script>
window.addEventListener("DOMContentLoaded", function(){
function dragStartFromPalette(event){
event.dataTransfer.setData("text/html",
'<original-element class="draggable_element">'+this.innerHTML+'</original-element>');
}
function dragEndFromPalette(event){
event.dataTransfer.effectAllowed = "copyMove";
event.dataTransfer.dropEffect = "copy";
event.dataTransfer.getData("text/plain");
event.preventDefault();
}
var light_tags = document.getElementsByClassName('draggable_element');
for(i = 0; i < light_tags.length; i++){
light_tags[i].addEventListener('dragstart', dragStartFromPalette);
light_tags[i].addEventListener('dragend', dragEndFromPalette);
};
}, false);
</script>
<style>
.draggable_element {
background-color: rgb(251, 214, 132);
}
</style>
<original-element class="draggable_element" draggable="true">Lorem</original-element>
<original-element class="draggable_element" draggable="true">ipsum</original-element>
<original-element class="draggable_element" draggable="true">dolor</original-element>
<div contenteditable="true">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Vel, illum reiciendis quas in quam eos iste inventore. Perferendis amet vel, ratione unde rerum? Quasi ea maxime incidunt, perferendis vero. Nesciunt.</div>
Here is my code https://jsfiddle.net/uzLb5xx0/
The first line will success. however, attempting to trying drag and drop to the second and subsequent lines will insert unrelated span element.
How can I insert all the elements as desired? help me ;(

Related

How to toggle the case of individual words in sentences displayed in HTML form input boxes by double clicking them?

Double clicking a word in a sentence in an HTML form input box will highlight that word. I want each double click to toggle the case of the highlighted word between lower and upper case.
<input id="vnib" type="text" value="#ATitle#">
Example: the title showing in the input box is "The longest Day".
When I double click on the word "longest" it will change to "Longest".
Why this? I need to correct hundreds of capitalization errors in a list of 30,000 titles and want to save hundreds of key presses. Right now I have to select the initial letters of each incorrectly capitalized word and then type in the letters with the correct case.
Here is a simple HTML + pure JS solution.
let content = document.getElementById("content");
content.addEventListener("dblclick", changeSelectionCase);
function changeSelectionCase(e) {
let selection = window.getSelection();
if (selection && selection.rangeCount > 0) {
let selectionRange = selection.getRangeAt(0);
let startOffset = selectionRange.startOffset
content.textContent = content.textContent.substring(0, startOffset) +
content.textContent[startOffset].toUpperCase() +
content.textContent.substring(startOffset + 1);
}
}
<p id="content">Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora nostrum optio suscipit cumque adipisci molestias inventore officia ea corrupti dolore alias nemo iure, beatae porro soluta quo aliquam ut facere.</p>
#Commata, per your comments, made it easier. Just save the below code as an HTML file and open it up with your browser. It contains an editable paragraph, so you can copy/paste your text on it. Then double click on the words you want to toggle the case of the first letter.
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
p:before {
content: "(Paste content here)";
font-weight: bold;
margin-right: 1rem;
}
p {
margin: 2rem;
padding: 2rem;
}
</style>
</head>
<body>
<p contenteditable="true" id="content">Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora nostrum
optio suscipit cumque adipisci molestias inventore officia ea corrupti dolore alias nemo iure, beatae porro
soluta quo aliquam ut facere.</p>
</body>
<script>
let content = document.getElementById("content");
content.addEventListener("dblclick", changeSelectionCase);
function changeSelectionCase(e) {
let selection = window.getSelection();
if (selection && selection.rangeCount > 0) {
let selectionRange = selection.getRangeAt(0);
let startOffset = selectionRange.startOffset;
let upperCase = content.textContent[startOffset].toUpperCase();
let toggledCase = upperCase === content.textContent[startOffset]
? content.textContent[startOffset].toLowerCase()
: content.textContent[startOffset].toUpperCase();
content.textContent = content.textContent.substring(0, startOffset) +
toggledCase +
content.textContent.substring(startOffset + 1);
}
}
</script>
</html>

Find element that's on the middle of the visible screen (viewport) to target another element

What I am aiming to achieve is something that looks like this. you can see it in action on this URL, if you scroll a bit.
I was thinking at first to try using inViewport, and every time a heading or a paragraph is in viewport to show one image and hide the previous. but my problem is that the elements are in viewport in conjunction
This is the initial code I was using:
$.fn.isInViewport = function () {
let elementTop = $(this).offset().top;
let elementBottom = elementTop + $(this).outerHeight();
let viewportTop = $(window).scrollTop();
let viewportBottom = viewportTop + window.innerHeight; // <-- here
return elementBottom > viewportTop && elementTop < viewportBottom;
};
$(window).scroll(function () {
if ($('.heading1 ').isInViewport()) {
// Use .blogcard instead of this
$('.img1').addClass('show');
} else {
// Remove class
$('.img1').removeClass('show');
}
if ($('.heading2 ').isInViewport()) {
// Use .blogcard instead of this
$('.img2').addClass('show');
} else {
// Remove class
$('.img2').removeClass('show');
}
});
I have found this answer but have no idea on how can I use is to my benefits.
I have also stumbled upon this solution, which looks even smarter, but the code adds the classes to the selector in viewport and not to a different element.
This is the code applied there:
ar getElementsInArea = (function(docElm){
var viewportHeight = docElm.clientHeight;
return function(e, opts){
var found = [], i;
if( e && e.type == 'resize' )
viewportHeight = docElm.clientHeight;
for( i = opts.elements.length; i--; ){
var elm = opts.elements[i],
pos = elm.getBoundingClientRect(),
topPerc = pos.top / viewportHeight * 100,
bottomPerc = pos.bottom / viewportHeight * 100,
middle = (topPerc + bottomPerc)/2,
inViewport = middle > opts.zone[1] &&
middle < (100-opts.zone[1]);
elm.classList.toggle(opts.markedClass, inViewport);
if( inViewport )
found.push(elm);
}
};
})(document.documentElement);
////////////////////////////////////
// How to use:
window.addEventListener('scroll', f)
window.addEventListener('resize', f)
function f(e){
getElementsInArea(e, {
elements : document.querySelectorAll('div'),
markedClass : 'highlight--1',
zone : [20, 20] // percentage distance from top & bottom
});
getElementsInArea(e, {
elements : document.querySelectorAll('div'),
markedClass : 'highlight--2',
zone : [40, 40] // percentage distance from top & bottom
});
}
Would love all the help I could get. Cheers
Using the IntersectionObserver API
It should be quite simple by using the IntersectionObserver API to watch for your elements intersecting the viewport, or any other (Options root) ancestor.
To detect an element reaches the viewport vertical center can be done by passing the Option rootMargin where the bottom and top values are set at -50%, with an Option threshold set to 0 (as soon as one pixel enters that intersecting area)
// Utility functions:
const EL = (sel, par) => (par || document).querySelector(sel);
const ELS = (sel, par) => (par || document).querySelectorAll(sel);
// App:
const ELS_pictures = ELS(".picture");
const switchPicture = (EL_entry) => {
const EL_picTarg = EL(EL_entry.dataset.reveal);
ELS_pictures.forEach(EL_pic => EL_pic.classList.toggle("is-active", EL_pic === EL_picTarg));
};
// In Viewport
const inViewport = (entries, observer) => entries.forEach(entry => entry.isIntersecting && switchPicture(entry.target));
// Assign observer to all Elements with data-reveal attribute (Articles)
ELS("[data-reveal]").forEach(el => {
const observer = new IntersectionObserver(inViewport, {
// root: (by default is Document),
rootMargin: "-50% 0px -50% 0px", // set the root intersecting area as a tiny line in the vertical center
threshold: 0, // 0 = as soon as 1px intersects
});
observer.observe(el);
});
/* QuickReset */
* {
margin: 0;
box-sizing: border-box;
}
body {
font: 18px/1.5 sans-serif;
}
.stickers {
display: grid;
grid-template-columns: 2fr 3fr;
max-width: 1000px;
margin: 0 auto;
}
/* Article Component */
.articles {
display: flex;
flex-direction: column;
}
.article {
margin: 100px 0;
padding: 30px;
}
.pictures {
position: sticky;
display: flex;
top: 0px;
right: 0;
height: 100vh;
background: #444;
}
.picture {
position: absolute;
height: 100%;
width: 100%;
object-fit: cover;
margin: auto;
transition: 0.3s opacity, 0.5s transform;
opacity: 0;
transform: scale(0.8);
}
.picture.is-active {
opacity: 1;
transform: scale(1);
}
<p style="height: 80vh;">Scroll down...</p>
<div class="stickers">
<div class="articles">
<div class="article" data-reveal="#picture_1">
<h1>Many cats</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Optio laudantium perferendis eius iusto vitae eaque, eligendi ullam, rerum maiores, totam velit! Debitis repudiandae aliquam placeat, minus. Facere nihil aspernatur nam!</p>
</div>
<div class="article" data-reveal="#picture_2">
<h1>Little cat</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur perferendis possimus asperiores deleniti voluptatum amet nostrum ratione odio, a perspiciatis suscipit ab nulla repellat laudantium praesentium adipisci! Nihil ex, quos!</p>
</div>
<div class="article" data-reveal="#picture_3">
<h1>Snowcat</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur perferendis possimus asperiores deleniti voluptatum amet nostrum ratione odio, a perspiciatis suscipit ab nulla repellat laudantium praesentium adipisci! Nihil ex, quos!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur perferendis possimus asperiores deleniti voluptatum amet nostrum ratione odio, a perspiciatis suscipit ab nulla repellat laudantium praesentium adipisci! Nihil ex, quos!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur perferendis possimus asperiores deleniti voluptatum amet nostrum ratione odio, a perspiciatis suscipit ab nulla repellat laudantium praesentium adipisci! Nihil ex, quos!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur perferendis possimus asperiores deleniti voluptatum amet nostrum ratione odio, a perspiciatis suscipit ab nulla repellat laudantium praesentium adipisci! Nihil ex, quos!</p>
</div>
<div class="article" data-reveal="#picture_4">
<h1>Cat</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut recusandae doloribus laboriosam, quasi aspernatur modi illum voluptate dicta alias optio, omnis qui deserunt. Quisquam, beatae dolores cum nostrum sint minima!</p>
</div>
</div>
<div class="pictures">
<img class="picture is-active" id="picture_1" src="https://placekitten.com/380/300" alt="Catz!">
<img class="picture" id="picture_2" src="https://placekitten.com/460/400" alt="Catz!">
<img class="picture" id="picture_3" src="https://placekitten.com/400/450" alt="Catz!">
<img class="picture" id="picture_4" src="https://placekitten.com/500/450" alt="Catz!">
</div>
</div>
<p style="height: 180vh;">Etc...</p>
Use data-* Attribute on your articles Elements where the value should match the selector of the related picture.
Toggle a class i.e: .is-active using classList that will determine the active styles for the matching picture Element
To make the fixed effect use position: sticky on your pictures parent element.

Scroll up after click a href in page

I have a footnote system. When I click sup number it scroll down to footnote. But It didn't show because of sticky header.
I need to scroll up around 60px after href scrolling.
I tried to add window.scrollBy(0, 60); in onclick of "sup a" object. It didn't work when I add it as function as well.
Full code:
$(document).ready(function() {
// make a link out of any <sup> with class .footnoted
$('sup').each(function(i){
var superscript = i+1;
$(this).html('<a>' + superscript + '</a>');
});
// give <sup class="footnoted"> an id to scroll back to
$('sup').each(function(i){
this.id = "reading-position-"+(i+1);
});
// tell the superscripts where to go
$('sup a').each(function(i){
this.href = "#footnote-"+(i+1);
});
// set a target for the superscripts to scroll to
// if you're not using a list for your footnotes, change li to the correct selector
$('ol li').each(function(i){
this.id = "footnote-"+(i+1);
});
// add return to reading position link at the end of the footnote
$('ol li').append('<a rel="nofoot"> ↑ Okuma Alanına Geri Dön</a>');
// give the return to position url an href of the target ID
$('ol li a').each(function(i){
this.href = "#reading-position-"+(i+1);
});
// make a back to top link at the end of your footnotes
// if you're not using a list for your footnotes, change li to the correct selector
// smooth scroll between reading position and footnotes
$('sup a[href^="#"]').on('click',function (e) {
e.preventDefault();
var target = this.hash,
$target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top - 60
}, 500, 'swing', function () {
window.location.hash = target;
});
// remove class and link to previous reading position from other <li> if you scrolled to a footnote previously
{#$('.current-footnote span').remove();#}
{#$('ol li').removeClass('current-footnote');#}
// add return to reading position link and give the current footnote a class for additional styles
$(target).addClass('current-footnote');
$('.current-footnote span').css('display', 'inline');
});
scrollBy(0, 60) scrolls down, not up. You need to use negative numbers to reverse the direction. So, window.scrollBy(0, -60).
If that still doesn't work, something is probably wrong with how you're registering the click handler. It would be useful if you could show your full code.
You can create references using the DATA attribute.
Example link: <span class="reference" data="#one">ONE</span>
This script will collect all elements with class class="reference" and add a listener to them. When the element is clicked, the script will take the DATA information which is the ID of the element to which the page should be scrolled.
In this line you can change the value 60 so that the information remains in the visible part
window.scrollTo(0, top - 60);
In this example, 60px is the height of the sticky navigation
var navLinks = document.querySelectorAll('.reference');
for (let i = 0; i < navLinks.length; i++) {
navLinks[i].addEventListener("click", function () {
var elId = this.getAttribute('data');
var top = document.querySelector(elId).offsetTop;
window.scrollTo(0, top - 60);
});
}
body {
margin: 0px;
}
nav {
height: 60px;
background: orangered;
position: fixed;
width: 100%;
top: 0px;
}
.reference {
cursor: pointer;
color: orangered;
}
#wrap {
margin-top: 60px;
}
<nav>Navigation...</nav>
<div id="wrap">
<div>
Lorem ipsum dolor, <span class="reference" data="#one">ONE</span> sit amet consectetur adipisicing elit. Cum nihil vero voluptatibus tempora repellendus <span class="reference" data="#two">TWO</span> aperiam quidem debitis, totam consequuntur ex aspernatur quasi quod molestias rem quibusdam? Facilis adipisci asperiores iste. Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum nihil vero voluptatibus tempora repellendus aperiam quidem debitis, totam consequuntur ex aspernatur quasi quod molestias rem quibusdam? Facilis adipisci asperiores iste. Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum nihil vero voluptatibus tempora repellendus aperiam quidem debitis, totam consequuntur ex aspernatur quasi quod molestias rem quibusdam? Facilis adipisci asperiores iste.
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
<p id="one">
ONE - Reference One
<br><br><br><br><br><br><br><br><br><br><br>
</p>
<p id="two">
TWO - Reference Two
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</p>
</div>

Rotate Arrow on Accordion Click

How do I get this arrow rotating when the parent accordion div is clicked? Any help would be wonderful. The idea is that as you click the accordion the arrow which is an svg file should rotate, however I can't seem to get it working
HTML
<button class="accordion">Design <img src="./assets/img/down.svg" alt="caret-down" class="caret"></button>
<div class="panel"><p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iste veritatis maiores doloribus ex, culpa tenetur?</p></div>
<button class="accordion">Development <img src="./assets/img/down.svg" alt="caret-down" class="caret"></button>
<div class="panel"><p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. At rerum a voluptate nesciunt! Quam, inventore.></div>
<button class="accordion">Search Engine Optimisation <img src="./assets/img/down.svg" alt="caret-down" class="caret"></button>
<div class="panel"><p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod, blanditiis. Corporis maxime eum nemo delectus.</p></div>
<button class="accordion">Progressive Web Apps <img src="./assets/img/down.svg" alt="caret-down" class="caret"></button>
<div class="panel"><p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Culpa consequuntur obcaecati iste odit vero mollitia.</p></div>
CSS
#services .accordion{
font-size:1rem;
background-color:rgba(245, 246, 250,0.4);
cursor:pointer;
padding:18px;
width:100%;
border:none;
outline:none;
display:flex;
justify-content:space-between;
width:68vw;
text-align:right;
}
#services .active, .accordion:hover{
background-color:rgba(245, 246, 250,0.4);
color:rgba(0, 151, 230,1.0);
}
#services .panel{
background-color:rgba(245, 246, 250,0.4);
display:none;
width:100%;
justify-content:center;
text-align:center;
}
#services .caret{
width:12px;
}
JS
/*accordion*/
var acc = document.getElementsByClassName("accordion");
var arrow = document.getElementsByClassName("caret")
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function () {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "flex") {
panel.style.display = "none";
} else {
panel.style.display = "flex";
}
arrow.setAttribute("transform", "rotate(-45deg)");
});
}
This currently throws an error at arrow.setAttribute, because arrow is not set at this point. And setAttribute is not a proper way to set inline styles.
Both can be fixed easily like this:
this.querySelector('.caret').style.transform = "rotate(-45deg)";
querySelector returns the first element with that class it finds, and by calling it on this, which refers to the button, we can automatically limit it to the correct scope. And then the inline style is set using an appropriate syntax.
(var arrow = document.getElementsByClassName("caret") can then be completely removed, you don’t need it for anything else.)
If you want the arrow to rotate back, when the element is collapsed again - then don’t place this line at the end, but in both the if and the else block. Once with rotate(-45deg), and once with rotate(0) to set it back to normal (or whatever value you need for that state.)

Show one or more elements and their content that have been hidden

I'm stuck and not sure how to move on. I want to be able to click a tab to reveal its content. With the code I currently have, when I click a single tab, it reveals the content for all the tabs. But I just want the click to reveal the content that's associated with that single tab. I'm looking for a vanilla javascript solution.
Here's the code: http://codepen.io/anon/pen/KCJAc (inline below)
CSS:
.tab-content {
display: block;
height: 0;
opacity: 0;
overflow: hidden;
transition: all 1s ease-out;
}
.tab-active {
height: auto;
opacity: 1;
}
JavaScript:
var tabHeaders = document.getElementsByClassName('tab-header');
for (var i = 0; i < tabHeaders.length; i++) {
tabHeaders[i].addEventListener('click', activateTab);
}
function activateTab() {
var tabContents = document.getElementsByClassName('tab-content');
for (var i = 0; i < tabContents.length; i++) {
tabContents[i].classList.add('tab-active');
}
}
HTML:
<div>
<h3 class="tab-header">Tab1</h3>
<p class="tab-content">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium odio iste aliquam molestias corporis blanditiis nihil soluta sint illum quibusdam reprehenderit sed quaerat iusto maiores error iure ducimus dicta ipsum.</p>
</div>
<div>
<h3 class="tab-header">Tab2</h3>
<p class="tab-content">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium odio iste aliquam molestias corporis blanditiis nihil soluta sint illum quibusdam reprehenderit sed quaerat iusto maiores error iure ducimus dicta ipsum.</p>
</div>
You're adding tab-active to all of the tab-content elements. You just want to add it to the one following the header that's clicked: Updated Pen
var tabHeaders = document.getElementsByClassName('tab-header');
for (var i = 0; i < tabHeaders.length; i++) {
tabHeaders[i].addEventListener('click', activateTab);
}
function activateTab() {
var tabContents = this.nextElementSibling;
while (tabContents && (!tabContents.classList || !tabContents.classList.contains("tab-content"))) {
tabContents = tabContents.nextElementSibling;
}
if (tabContents) {
tabContents.classList.toggle("tab-active");
}
}
Notes:
I'm using nextElementSibling to get the next sibling that's an element since you used classList in the original, so I figure you're only using this code on fairly up-to-date browsers. If you intend to use it on older browsers, you can use nextSibling instead (and also use className rather than classList.
This is because by using document.getElementsByClassName you are getting all tab-content tabs in your page, rather than the DOM level of the clicked tab-header element. You can use the nextSibling property of tab-header to get the next DOM node beside tab-header:
function activateTab() {
this.nextSibling.classList.add("tab-active");
}
Or, if you are not sure if tab-content will definitely appear directly after tab-header you can query parentNode using querySelector:
function activateTab() {
this.parentNode.querySelector(".tab-content").classList.add("tab-active");
}
Note this last method won't work in anything lower than IE8, but then again neither will the classList property in your original question.

Categories

Resources