Get the cursor out of span inside contenteditable - javascript

In the following snippet if the user types something after of TEST it automatically becomes red. This is because user typed text is going inside the span. While it may be the desired behaviour sometimes, often user wants to type a new word and get out of span.
Is there a way to toggle between the two behaviours. Ideally, I would want that if the user presses right arrow key, they should get out of span.
.bg-red{
background: red;
}
<div id="wrapper" contenteditable="true">
<span class="bg-red">TEST</span>
</div>

you can try like this
.bg-red{
background: red;
}
<div id="wrapper" >
<span class="bg-red" style="float:left" contenteditable="true">TEST</span> <span contentEditable="true" style="float:left">Your Comment</span>
</div>

Dear try below its simple.
$(document).ready(function () {
$('<span id="newline"> </span>').insertAfter('.bg-red');
$('#wrapper').click(function () {
if ($("#newline").length) {
$("#newline").focus();
}
else {
$("#newline").focus();
}
});
$("#wrapper").on("keypress", function () {
$("#newline").focus();
})
});
.bg-red {
background: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="wrapper" contenteditable="true">
<span class="bg-red">TEST</span>
</div>

Here is a Pure JS execution of what you requested.
You add an event listener to listen for keypress.
You identify the key you desire.
Execute whatever you want.
Best Wishes
document.addEventListener('keydown', indentifyKeyPressed);
const wrapper = document.getElementById("wrapper");
function indentifyKeyPressed(event) {
if (event.key === "ArrowRight") {
//Creating a new element
const newElement = document.createElement("span")
newElement.setAttribute("class", "bg-green")
newElement.setAttribute("contenteditable", "true")
wrapper.appendChild(newElement).focus()
}
}
.bg-red {
background: red;
}
.bg-green {
background: green;
}
<div id="wrapper" >
<span class="bg-red" contenteditable="true" >TEST</span>
</div>

Related

How to retrieve the current element with addEventListener based on class?

I am using a function to retrieve the current element that receives an event (a click in this case), but I'd like to remove the function from my HTML script and use an event listener to do the same, however I am using a class to aim the elements. If I had only one element it would be easy, but that's not the case.
My first thought was to use querySelector, but it gives me only the first element, as it should be.
I could use querySelectorAll, but in that case I believe I would need an index or something like that, what implies a loop and, therefore, a function inside the HTML script, which is exactly what I am trying to avoid in this scenery.
In short, if I click on the first element I want to retrieve it, if I click on the second I want the second and so on, but I'd like to do that within the same event listener.
Is there a simple way to do that?
P.S. I don't want to use IDs.
//This is what I'm doing:
function getElement() {
const element = event.currentTarget
console.log(element)
}
//This is what I want to do:
/*
const elements = document.querySelector('.class-example')
elements.addEventListener('click', (e) => console.log(e.currentTarget))
*/
.class-example {
cursor: pointer;
margin-bottom: 10px;
}
.class-example:first-of-type {
background-color: red;
}
.class-example:nth-of-type(2) {
background-color: yellow;
}
.class-example:nth-of-type(3) {
background-color: green;
}
.class-example:nth-of-type(4) {
background-color: blue;
color: white;
}
<div onClick="getElement()" class="class-example" >Div 1</div>
<div onClick="getElement()" class="class-example" >Div 2</div>
<div onClick="getElement()" class="class-example" >Div 3</div>
<div onClick="getElement()" class="class-example" >Div 4</div>
Afaik, it's not possible to put an event listener on multiple elements at once unless you loop over them and attach one on each element. You can however simply put an event listener on the whole document and then only match what you need:
document.addEventListener('click',function(e){
if(e.target && e.target.classList.contains('class-example')){
//do something
}
});
This has the added benefit that it'll work for dynamically added elements as well.
Edit (thanks to Mitya): This is known as event delegation. If you're interested in more information about this, Mitya provides a guide here.
There are two possible approaches ...
... one either does subscribe each element to the event one does wish to be notified about and wants to handle ...
function getExampleNode(evt) {
const elmNode = evt.currentTarget;
console.log(elmNode);
return elmNode;
}
function mainInit() {
document
.querySelectorAll('.class-example')
.forEach(elmNode =>
elmNode.addEventListener('click', getExampleNode)
);
}
mainInit();
.class-example {
cursor: pointer;
margin-bottom: 5px;
}
.class-example:first-of-type {
background-color: red;
}
.class-example:nth-of-type(2) {
background-color: yellow;
}
.class-example:nth-of-type(3) {
background-color: green;
}
.class-example:nth-of-type(4) {
background-color: blue;
color: white;
}
.as-console-wrapper { max-height: 50%!important; }
<div class="container">
<div class="A class-example">
<span>within ...
<span>div 1</span>
</span>
</div>
<div class="B class-example">
<span>within ...
<span>div 2</span>
</span>
</div>
<div class="C class-example">
<span>within ...
<span>div 3</span>
</span>
</div>
<div class="D class-example">
<span>within ...
<span>div 4</span>
</span>
</div>
</div>
... or one makes use of event delegation where one does subscribe just a single grouping element to this very event.
function getExampleNode(evt) {
const containerNode = evt.currentTarget;
const targetNode = evt.target; // either <span/> or <div/>
let exampleNode;
let isInExample;
Array.from(
document.querySelectorAll('.class-example')
).some(elmNode => {
isInExample = (elmNode === targetNode) || elmNode.contains(targetNode);
if (isInExample) {
exampleNode = elmNode;
}
return isInExample;
});
console.log('targetNode :', targetNode);
console.log('exampleNode :', exampleNode);
console.log('\n');
return exampleNode;
}
function mainInit() {
document
.querySelector('.container')
.addEventListener('click', getExampleNode);
}
mainInit();
.class-example {
cursor: pointer;
margin-bottom: 5px;
}
.class-example:first-of-type {
background-color: red;
}
.class-example:nth-of-type(2) {
background-color: yellow;
}
.class-example:nth-of-type(3) {
background-color: green;
}
.class-example:nth-of-type(4) {
background-color: blue;
color: white;
}
.as-console-wrapper { max-height: 50%!important; }
<div class="container">
<div class="A class-example">
<span>within ...
<span>div 1</span>
</span>
</div>
<div class="B class-example">
<span>within ...
<span>div 2</span>
</span>
</div>
<div class="C class-example">
<span>within ...
<span>div 3</span>
</span>
</div>
<div class="D class-example">
<span>within ...
<span>div 4</span>
</span>
</div>
</div>
The first approach's advantage is its simple event handling for one always can be sure about the event's currentTarget which equals one of the elements one was subscribing to the very event.
The disadvantage comes less from the amount of event listeners one did create in first place, but more from the possible loss of handling information in case one has to deal with elements which are added later to the DOM.
The latter scenario gets covered by the second approach. The event registration here is much simpler, but the implementation of the event handler is a little bit more complex.
One has to provide additional logic which searches for the desired element by figuring out wich of the possible elements does contain or equal the event's currentTarget.

jQuery - detect if div has 2 classes

I am using jQuery to detect a click like this..
$(".clickable_link").click(function() {
console.log('Link Clicked');
}
<div class="clickable_link">
Click Me
</div>
<div class="clickable_link special">
Click Me
</div>
I am trying to determine if the div with 'special' has been clicked or if it just the div with 'clickable_link'.
What is the best way to do this? Should I use hasclass or is filter a better choice?
Something like this:
$(".click").click(function(){
if ($(this).hasClass("special")) {
alert("Its Special!");
}
});
.click {
width:100px;
height:50px;
background-color:#333;
float:left;
margin:10px;
color:#fff;
text-align:center;
padding-top:25px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="click">Not Special</div>
<div class="click special">SPECIAL!</div>
As an alternative to .hasClass, you can use .is, which allows for any selector, not just checking for a class.
if($(this).is(".special")) { ...
$(".clickable_link").click(function() {
if ($(this).is(".special")) {
alert("special clicked");
} else {
alert("nope");
}
});
.special { color: red; }
.clickable_link { cursor: pointer; margin-bottom: 0.5em; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="clickable_link">
Click Me
</div>
<div class="clickable_link special">
Click Me
</div>
$('#id1').click(function() {
var x = $('#id1').attr('class') //dont use classname to fetch the element
x = x.split(' ')
if (x.length > 2)
alert('id1 has more than 2 classes');
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='id1' class='myclass mysubclass'>dfdfdfsdfds</div>
You can bind different event handlers depending on whether the special class exists.
$(".clickable_link.special").click(function()
console.log("Special link clicked");
})
$(".clickable_link:not(.special)").click(function() {
console.log("Ordinary link clicked");
});
If there's common code for both types of links, you can put that in another function that gets called by each handler.
As example, use can can detect count of classes as like it:
$(".clickable_link").click(function() {
var classList = $(this).attr('class').split(/\s+/);
console.log(`count ${classList.length}`);
}

Insert HTML or append Child onClick, and remove from previous clicked element

I have a set of div elements inside a container, .div-to-hide is displayed by default whilst .div-to-show is hidden.
When I click in .set, .div-to-hide should hide and .div-to-show should be visible. Next click should return the previous clicked element to its default state.
I need to display to buttons on click inside on .div-to-show.
<div class="container">
<div class="set">
<div class="div-to-hide">Some text</div>
<div class="div-to-show"></div>
</div>
<div class="set">
<div class="div-to-hide">Some text</div>
<div class="div-to-show"></div>
</div>
<div class="set">
<div class="div-to-hide">Some text</div>
<div class="div-to-show"></div>
</div>
</div>
So far I have this:
let lastClicked;
$('.container').on('click', function(e) {
if (this == lastClicked) {
lastClicked = '';
$('.div-to-hide').show();
$(this).children('.div-to-hide').hide();
} else {
lastClicked = this;
$('.div-to-hide').hide();
$(this).children('.div-to-hide').show();
$(this).children('.div-to-show').hide();
}
});
Can't get it to work properly tho.. I don't know what I am missing...
Any help is deeply appreciated!
UPDATE: got it working! Thanks everyone!
First, you are not using delegation (second parameter on the $.on() function) to define the .set element as your this inside the function.
If I understood correctly, you want to show the elements on the last one clicked and hide the rest. You don't really need to know which one you last clicked to do that
$('.container').on('click', '.set', function (e) {
// Now "this" is the clicked .set element
var $this = $(this);
// We'll get the children of .set we want to manipulate
var $div_to_hide = $this.find(".div-to-hide");
var $div_to_show = $this.find(".div-to-show");
// If it's already visible, there's no need to do anything
if ($div_to_show.is(":visible")) {
$div_to_hide.show();
$div_to_show.hide();
}
// Now we get the other .sets
var $other_sets = $this.siblings(".set");
// This second way works for more complex hierarchies. Uncomment if you need it
// var $other_sets = $this.closest(".container").find(".set").not(this);
// We reset ALL af them
$other_sets.find(".div-to-show").hide();
$other_sets.find(".div-to-hide").show();
});
Consider using class toggling instead.
$('.set').on('click', function(e) {
$('.set').removeClass('hidden-child');
$(this).addClass('hidden-child');
});
css:
.hidden-child .div-to-hide, .div-to-show {
display: none;
}
.hidden-child .div-to-show, .div-to-hide {
display: block;
}
This will make your code easier to reason about, and lets css control the display (style) rules.
Edit: changed class name for clarity; expanded explanation; corrected answer to conform to question
Try to make use of siblings() jQuery to hide and show other divs and toggle() jQuery to show and hide itself and also you will need to set click() event on .set, not in .container
$(document).on('click', '.set', function(e) {
$(this).find('.hide').toggle();
$(this).find('.show').toggle();
$(this).siblings('.set').find('.hide').show();
$(this).siblings('.set').find('.show').hide();
});
.show {
display: none;
}
.set div {
padding: 10px;
font: 13px Verdana;
font-weight: bold;
background: red;
color: #ffffff;
margin-bottom: 10px;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="set">
<div class="hide">1 Hide</div>
<div class="show">1 Show</div>
</div>
<div class="set">
<div class="hide">2 Hide</div>
<div class="show">2 Show</div>
</div>
<div class="set">
<div class="hide">3 Hide</div>
<div class="show">3 Show</div>
</div>
</div>

Javascript - show a button inside of a DIV when clicked (and hide all others)

I have a list of DIVS that have buttons inside. By default, all buttons are hidden. When I click within a DIV area, the current button inside of this clicked DIV are should show (class='.db') AND all previously clicked/shown buttons should be hidden (class='.dn'). In other words, at any time there should be only one button (currently clicked) shown and all other should be hidden.
I want to use vanilla Javascript and tried this below, but it won't work. I feel there is some small error but don't know where.. Note - the DIVS and buttons don't have their own unique IDs (they only have the same CSS (.posted) classes.
PS - maybe it'd be better not to add this onClick="t();" to each DIV and use an 'addEventListener' function, but this is way too much for me ; )
CSS:
.dn {display:none}
.db {display:block}
.posted {
height: 50px;
width: 100px;
background-color: green;
border: 2px solid red;
}
HTML:
<div class="posted" onClick="t();">
<button class="dn">Reply</button>
</div>
<div class="posted" onClick="t();">
<button class="dn">Reply</button>
</div>
<div class="posted" onClick="t();">
<button class="dn">Reply</button>
</div>
JAVASCRIPT:
function t()
{
var x=document.getElementsByClassName("posted"),i,y=document.getElementsByTagName("button");
for(i=0;i<x.length;i++)
{
x[i].y[0].className="dn";
};
x.y[0].className='db';//make sure the currently clicked DIV shows this button (?)
}
You might want to read more about selector, how to select class, block level etc.
some link might be helpful:
CSS selector:
https://www.w3schools.com/cssref/css_selectors.asp
jQuery selector:
https://api.jquery.com/category/selectors/
Solution - Using jQuery:
$('.posted').on('click', function() {
//find all class called posted with child called dn, then hide them all
$('.posted .dn').hide();
//find this clicked div, find a child called dn and show it
$(this).find('.dn').show();
});
.dn {
display: none
}
.db {
display: block
}
.posted {
height: 50px;
width: 100px;
background-color: green;
border: 2px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="posted">
<button class="dn">Reply1</button>
</div>
<div class="posted">
<button class="dn">Reply2</button>
</div>
<div class="posted">
<button class="dn">Reply3</button>
</div>
Solution - Pure js version:
//get list of div block with class="posted"
var divlist = Array.prototype.slice.call(document.getElementsByClassName('posted'));
//for each div
divlist.forEach(function(item) {
//add click event for this div
item.addEventListener("click", function() {
//hide all button first
divlist.forEach(function(el) {
el.getElementsByTagName('button')[0].classList.add('dn');
});
//show button of the div clicked
this.getElementsByTagName('button')[0].classList.remove('dn');
}, false);
});
.dn {
display: none
}
.db {
display: block
}
.posted {
height: 50px;
width: 100px;
background-color: green;
border: 2px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="posted">
<button class="dn">Reply1</button>
</div>
<div class="posted">
<button class="dn">Reply2</button>
</div>
<div class="posted">
<button class="dn">Reply3</button>
</div>
You can do this with with plain JavaScript using Event Bubbling, querySelector and the element classList attribute like this.
Change your HTML to look like this:
<div class="posts">
<div class="posted">
<button class="dn">Reply</button>
</div>
<div class="posted" >
<button class="dn">Reply</button>
</div>
<div class="posted" >
<button class="dn">Reply</button>
</div>
</div>
Then use JavaScript like this:
var posts = document.querySelector('.posts');
var allPosted = document.querySelectorAll('.posted');
//clicks bubble up into the posts DIV
posts.addEventListener('click', function(evt){
var divClickedIn = evt.target;
//hide all the buttons
allPosted.forEach(function(posted){
var postedBtn = posted.querySelector('button');
postedBtn.classList.remove('db');
});
// show the button in the clicked DIV
divClickedIn.querySelector('button').classList.add('db')
});
You can find a working example here: http://output.jsbin.com/saroyit
Here is very simple example using jQuery .siblings method:
$(function () {
$('.posted').click(function () {
$('button', this).show();
$(this).siblings().find('button').hide();
});
});
https://jsfiddle.net/3tg6o1q7/

Event handler for loop with dynamic jQuery selectors

What I want is to click on #bt-1 and change the color of #target-1, click on #bt-2 and change the color of #target-2...
I started writing a particular click event handler for each #bt-n / #target-n but as the site got bigger I thought about using a loop. My approach was using a for loop with variables in jQuery selectors. Here is my code:
$(document).ready(function() {
var total = $('.target').length;
for(n=1; n<=total; n++) {
var num = String(n);
$('#bt-'+num).on('click', function() {
$('#target-'+num).toggleClass('yellow');
});
}
});
.wrapper {
display: flex;
text-align: center;
}
.button, .target {
padding: 20px;
margin: 10px;
}
.button {
background: gray;
}
#target-1 {
background: red;
}
#target-2 {
background: green;
}
#target-3 {
background: blue;
}
.yellow {
background: yellow !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div id="bt-1" class="button">
<h1>Button 1</h1>
</div>
<div id="target-1" class="target">
<h1>Target 1</h1>
</div>
</div>
<div class="wrapper">
<div id="bt-2" class="button">
<h1>Button 2</h1>
</div>
<div id="target-2" class="target">
<h1>Target 2</h1>
</div>
</div>
<div class="wrapper">
<div id="bt-3" class="button">
<h1>Button 3</h1>
</div>
<div id="target-3" class="target">
<h1>Target 3</h1>
</div>
</div>
I don't understand why it only targets the last #target-n as the loop seems to be working on #bt-n. I also thought about using an array but can't figure out how to implement it.
I managed to make it work using $(this).siblings('.target')... which do not require the for loop and ids but a parent element for each .button / .target, in this case .wrapper Code Here. Although this was a good solution, I would like to understand what I did wrong and how to properly implement a loop to achieve this without using the parent .wrapper. Thank you.
The reason that only the last item gets affected is because the loop has completed before any event fires. Therefore n holds the last value in the loop. To fix this you need to use a closure:
for (n = 1; n <= total; n++) {
(function(n) {
$('#bt-' + n).on('click', function() {
$('#target-' + n).toggleClass('yellow');
});
})(n);
}
That said, a much better approach would be avoid the loop and to use DOM traversal to find the .target related to the clicked .button, like this:
$('.button').click(function() {
$(this).next('.target').toggleClass('yellow');
});
.wrapper {
display: flex;
text-align: center;
}
.button,
.target {
padding: 20px;
margin: 10px;
}
.button {
background: gray;
}
#target-1 {
background: red;
}
#target-2 {
background: green;
}
#target-3 {
background: blue;
}
.yellow {
background: yellow !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div id="bt-1" class="button">
<h1>Button 1</h1>
</div>
<div id="target-1" class="target">
<h1>Target 1</h1>
</div>
</div>
<div class="wrapper">
<div id="bt-2" class="button">
<h1>Button 2</h1>
</div>
<div id="target-2" class="target">
<h1>Target 2</h1>
</div>
</div>
<div class="wrapper">
<div id="bt-3" class="button">
<h1>Button 3</h1>
</div>
<div id="target-3" class="target">
<h1>Target 3</h1>
</div>
</div>
It is unwise to register a lot of event handlers. You can bind one event handler and perform action for given specific idx read from element id, eg:
$('body').on('click', function (event) {
if (!event.target.id.match(/^bt-\d+/)) {
return; //id of clicked element does not match bt-{number}
}
var idx = event.target.id.replace('bt-', ''); //remove prefix "bt-" and leave only numeric postfix
$('#target-' + idx).toggleClass('yellow');
});
Explanation:
When you bind click on body element You are getting access to all click events from child elements that not cancelled passing that event up. Element that has been clicked in saved inside event.target and it has property id in event.target.id.
On this id property I call match function with regular expression - it will match string which starts ^ from bt- and have any number \d at least one one + .
if (!event.target.id.match(/^bt-\d+/)) {
return; //id of clicked element does not match bt-{number}
}
There is negation of this statement, so If this id is not in format bt-someNumber it will not go further.
var idx = event.target.id.replace('bt-', '');
Than takes id and replaces bt- part in it with empty string ''
$('#target-' + idx).toggleClass('yellow');
Finally You are toggling class on element with same number as button but with different prefix target- instead of bt-.
Happy hacking!

Categories

Resources