How to remove highlighted multiple html contenteditable elements? - javascript

How can I highlight multiple HTML elements and remove them by pressing the Backspace key? WordPress block editor and Editor.js has this feature. Users can select/ Highlight multiple block and remove them by pressing the Backspace key.
From this code below, If I highlight by selecting(mouse) not by clicking the .block-1, .block-2, .block-3 and press Backspace key then it should delete these elements.
[NOTE]
Every block should have contenteditable attribute.
Please, go to Editor.js first for demo what I exact want.
<div class="block-1" contenteditable="true"> 1st Block </div>
<div class="block-2" contenteditable="true"> 2nd Block </div>
<div class="block-3" contenteditable="true"> 3rd Block </div>
<div class="block-4" contenteditable="true"> 4th Block </div>

We need 2 listeners:
1 - on mouseup, which catches selected text, and use TreeWalker to get all the highlighted elements and toggle selected class on .blocks.
2 - on keyup, which will catch backspace
Edit:
Used This and improved the answer .
$(document).on({
'keyup': function(e) {
if (e.which == 8)
$('div.block.selected').remove();
},
'mouseup': getSelectedElementTags
});
function rangeIntersectsNode(range, node) {
var nodeRange;
if (range.intersectsNode) {
return range.intersectsNode(node);
} else {
nodeRange = node.ownerDocument.createRange();
try {
nodeRange.selectNode(node);
} catch (e) {
nodeRange.selectNodeContents(node);
}
return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
}
}
function getSelectedElementTags() {
var win = window;
var range, sel, elmlist, treeWalker, containerElement;
sel = win.getSelection();
if (sel.toString().length == 0) return
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0);
}
if (range) {
containerElement = range.commonAncestorContainer;
if (containerElement.nodeType != 1) {
containerElement = containerElement.parentNode;
}
treeWalker = win.document.createTreeWalker(
containerElement,
NodeFilter.SHOW_ELEMENT,
function(node) {
return rangeIntersectsNode(range, node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
},
false
);
elmlist = [treeWalker.currentNode];
while (treeWalker.nextNode()) {
elmlist.push(treeWalker.currentNode);
}
elmlist.forEach(function(e) {
if ($(e).hasClass('block')) {
$(e).toggleClass('selected');
}
});
sel.empty()
}
}
div.block.selected {
background-color: #ddf;
}
div.block {
margin: 24px;
border-bottom: 1px solid #ddd;
font-size: 13px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container" contenteditable="true">
<div class="block block-1" contenteditable="true"> 1st Block</div>
<div class="block block-2" contenteditable="true"> 2nd Block </div>
<div class="block block-3" contenteditable="true"> 3rd Block</div>
<div class="block block-4"> 4th Block </div>
</div>

The CSS and the contenteditable attribute are not necessary. Here is the code
var targeted;
document.getElementById('container').addEventListener('click', function(event) {
console.log(event.target); // You can see here the targeted element
if(event.target.id !== 'container') {
targeted = event.target;
} else {
targeted = undefined;
};
});
document.addEventListener('keydown', function(event) {
if(event.keyCode === 8 && targeted) {
targeted.parentNode.removeChild(targeted);
};
});
#container {
padding: 20px;
background-color: black;
}
#container div {
margin-bottom: 5px;
height: 50px;
width: 200px;
background-color: white;
}
<div id="container">
<div contenteditable="true">1</div>
<div contenteditable="true">2</div>
<div contenteditable="true">3</div>
<div contenteditable="true">4</div>
<div contenteditable="true">5</div>
</div>

Related

Change classes only for the child clicked within the parent using JavaScript

I have two div's within a parent div. I need to change the classes for the child div which I clicked. For that I am writing a method to check which child was clicked and respectively I am trying to hide the other child div.
But I am not able to add classes or remove classes since the index is showing always as undefined. I am feeling there is some problem with the return statement.
function changeClass() {
const list = document.getElementById('my_div').children;
const indx = this.getIndexOfParent(list);
for (let i = 0; i < list.length; i++) {
if (indx === 0) {
list[indx + 1].classList.add("d-none d-sm-block");
list[indx].classList.remove("col-6 d-none d-sm-block");
} else if (indx === 1) {
list[indx - 1].classList.add("d-none d-sm-block");
list[indx].classList.remove("col-6 d-none d-sm-block");
}
list[indx].classList.add("d-xs-block");
}
}
function getIndexOfParent(child_list) {
for (var i = 0, len = child_list.length; i < len; i++) {
((index) => {
child_list[i].onclick = () => {
return index;
};
})(i);
}
}
.row {
background: #f8f9fa;
margin-top: 20px;
}
.row > div {
border: solid 1px black;
padding: 10px;
}
<div class="container">
<div class="row">
<div onclick="changeClass()" class="col-md-6 col-6">
child-div-1
</div>
<div onclick="changeClass()" class="col-md-6 col-6">
child-div-2
</div>
</div>
</div>
All I want is that, when I click on child-div-1 it should hide child-div-2 and vice versa only for small screens (which is why I am handling it by col-6 and d-xs-block classes)
Can anyone help me to solve the below problem.
You have added onclick within the for loop. Instead add the class to the clicked child div and remove the class from it's sibling div.
document.querySelectorAll('div.row > div')
.forEach((div) => {
div.addEventListener('click', function({
target
}) {
target.classList.add('d-none', 'd-sm-block');
const sibDiv = Array.prototype.filter.call(target.parentNode.children, div => div != target)[0];
sibDiv.classList.remove('col-6', 'd-none', 'd-sm-block');
});
});
.row {
background: #f8f9fa;
margin-top: 20px;
}
.row>div {
border: solid 1px black;
padding: 10px;
}
<div class="container">
<div class="row">
<div class="col-md-6 col-6">
child-div-1
</div>
<div class="col-md-6 col-6">
child-div-2
</div>
</div>
</div>
-- Edit --
The return statement will return the value of index to the callback function, you also need to add return to the callback function, so whatever result the callback function get will return to the function getIndexOfParent.
function getIndex() {
let i = 0;
((index) => { // No return, logs undefined
return index;
})(i);
}
console.log(getIndex());
function getIndex() {
let i = 0;
return ((index) => { // with return
return index;
})(i);
}
console.log(getIndex());
I made a pen for solving this problem.
Check the pen here
the solution is easy you simply have to write this line
e.stopPropagation();
this will stop the event from triggering on parent divs
basically what you are describing is called event bubbling.
you can read about it more on medium

Switch classes on click next or back

I'm trying to setup multiple-step form in which the first step is visible by default and rest of the steps are hidden with class "hide". I'd like to switch the class with Next and Back button so only one step is visible at a time. Could you please help with this (Already spent an hour on this)
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
$('.next').click(function(){
$('div:not(.hide)').next().removeClass('hide');
$('.hide').prev().removeClass('hide')
})
Try combining the 2 actions into one, like so:
$('.next').click(function(){
$('.steps div:not(.hide)').addClass('hide').next().removeClass('hide');
})
That way, you add the .hide class on your current div and then remove it on the next one.
You can use something similar for the Back button, by replacing .next() with .previous()
$('.next').click(function() {
// find the div that is not hidden
var $current = $('.steps div:not(.hide)');
// only perform logic if there is a proceeding div
if ($current.next().length) {
// show the next div
$current.next().removeClass('hide');
// hide the old current div
$current.addClass('hide')
}
});
$('.back').click(function() {
// find the div that is not hidden
var $current = $('.steps div:not(.hide)');
// only perform logic if there is a preceeding div
if ($current.prev().length) {
// show the previous div
$current.prev().removeClass('hide');
// hide the old current div
$current.addClass('hide')
}
});
.hide { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
You can add a current step variable to track the currently displayed step and two css for styling and showing your content.
jQuery(function($) {
let currentstep = 1;
let maxsteps = 4;
function showstep(step) {
let step_c = '.step' + step;
for (i = 1; i <= maxsteps; i++) {
var step_selector = '.step' + i;
$(step_selector).removeClass('show');
$(step_selector).addClass('hide');
}
$(step_c).removeClass('hide');
$(step_c).addClass('show');
};
$('.next').click(function() {
currentstep = currentstep + 1;
currentstep = (currentstep % (maxsteps + 1));
if (currentstep == 0) currentstep = 1;
showstep(currentstep);
});
$('.back').click(function() {
currentstep = currentstep - 1;
currentstep = (currentstep % (maxsteps + 1));
if (currentstep == 0) currentstep = 4;
showstep(currentstep);
});
});
.hide {
display: none;
}
.show {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1 show">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="back">Back</div>
<div class="next">Next</div>
I converted Taplar's answer to a jQuery plugin.
You are essentially navigating left or right by one, using the previous and next functions. These functions navigate through the sibling elements.
(function() {
$.fn.moveRight = function(className) {
var $curr = this.find('div:not(.' + className + ')');
if ($curr.next().length) $curr.next().removeClass(className);
else this.find('div:first-child').removeClass(className);
$curr.addClass(className);
return this;
};
$.fn.moveLeft = function(className) {
var $curr = this.find('div:not(.' + className + ')');
if ($curr.prev().length) $curr.prev().removeClass(className);
else this.find('div:last-child').removeClass(className);
$curr.addClass(className);
return this;
};
})(jQuery);
$('.next').on('click', (e) => $('.steps').moveRight('hide'));
$('.back').on('click', (e) => $('.steps').moveLeft('hide'));
.hide {
display: none;
}
.nav {
width: 260px;
text-align: center;
}
.nav .nav-btn::selection { background: transparent; }
.nav .nav-btn::-moz-selection { background: transparent; }
.nav .nav-btn {
display: inline-block;
cursor: pointer;
}
.steps {
width: 260px;
height: 165px;
border: thin solid black;
text-align: center;
line-height: 165px;
font-size: 3em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="steps">
<div class="step1">step1</div>
<div class="step2 hide">step2</div>
<div class="step3 hide">step3</div>
<div class="step4 hide">step4</div>
</div>
<div class="nav">
<div class="nav-btn back">[ << Back ]</div>
<div class="nav-btn next">[ Next >> ]</div>
</div>

remove class to sibling of elemnt jquery

I have this Jquery function, with a filter that adds a class named selected to the filter by click and shows all that data filter tags of the selected filter.
I want to define that if the sibling of the chosen element has a class named selected, that class needs to be removed from the rest and has to be added to only the selected element.
Function Script
(function ($) {
"use strict";
$.fn.filter = function (options) {
var defaults = {
nav: '[data-filter]' //
}
var $this = this,
settings = $.extend(defaults, options),
$target = $(settings.target),
selected = [];
return this.each( function() {
var $element = $(this);
$(settings.nav).each( function() {
$(this).click( function(event) {
// add selected class
$(this).toggleClass('selected');
// manipulate selected terms array
if ($.inArray($(this).data('filter'), selected) < 0 ) {
selected.push($(this).data('filter'));
} else {
var index = $.inArray($(this).data('filter'), selected);
selected.splice(index, 1);
}
// show/hide elements
$element.find('[data-filter-tags]').each( function() {
var terms = $(this).data('filter-tags').split(','),
show = null;
for (var i=0;i<selected.length;i++) {
show = ($.inArray(selected[i], terms) >= 0 && show !== false);
}
if (show || selected.length == 0) {
$(this).fadeIn();
} else {
$(this).fadeOut();
}
});
event.preventDefault();
});
});
});
};
}(jQuery));
HTML
<div id="tags">
<div id="cities" data-activeclass="selected">
תל אביב
רמת גן
הכל
<div>
</br>
משרה מלאה
משרה חלקית
הכל
</br>
מזכירות
הפעלה
הכל
</br>
</nav>
<div id="filter">
<div class="block" style="background: green" data-filter-
tags="time,kind,city,telaviv,full,sec">תל אביב משרה מלאה מזכירות</div>
<div class="block" style="background: blue" data-filter-
tags="time,kind,city,ramatgan,full,sec">רמת גן מלאה מזכירות</div>
<div class="block" style="background: blue" data-filter-
tags="time,kind,city,ramatgan,part,op">רמת גן חלקית הפעלה</div>
<div class="block" style="background: blue" data-filter-
tags="time,kind,city,telaviv,full,op">תל אביב מלאה הפעלה</div>
<div class="block" style="background: blue" data-filter-
tags="time,kind,city,ramatgan,part,sec">רמת גן חלקית מזכירות</div>
</div>
I want to define that if the sibling of the choosen elemnts has class named
"selected" remove the class from them and add it only to the selected element.
If clicking the element selects it and deselects all of its siblings, then in your click handler:
$(this).addClass("selected").siblings().removeClass("selected");
Simplified live example:
$("[data-filter]").on("click", function() {
$(this).addClass("selected").siblings().removeClass("selected");
});
[data-filter] {
border: 1px solid blue;
}
.selected {
background-color: yellow;
}
<div>
<span data-filter="1">one</span>
<span data-filter="2">two</span>
<span data-filter="3">three</span>
<span data-filter="4">four</span>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
If clicking the element toggles it, we want toggleClass (which you have) but the rest is the same:
$(this).toggleClass("selected").siblings().removeClass("selected");
...since the .siblings().removeClass("selected"); part just won't do anything if the current element was the one that was selected.
Simplified live example:
$("[data-filter]").on("click", function() {
$(this).toggleClass("selected").siblings().removeClass("selected");
});
[data-filter] {
border: 1px solid blue;
}
.selected {
background-color: yellow;
}
<div>
<span data-filter="1">one</span>
<span data-filter="2">two</span>
<span data-filter="3">three</span>
<span data-filter="4">four</span>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Can I execute a different set of code on the second time an event is triggered?

I am trying to develop a menu that can be navigated from the arrow keys and am having some trouble figuring out where to go from the initial event trigger that "highlights" the first element. If you check out my fiddle, you'll find that the first element is highlighted as it should when the right arrow key is pressed (remember to click in the body section of the fiddle!), but I am not sure where to go from there so that subsequent key presses will cycle through all of the elements.
$(document).ready(function($) {
$("body").keydown(function(event) {
if (event.which == 39) {
$(".a").css({
"outline": "3px solid red"
});
}
});
});
.tile {
width: 100px;
height: 100px;
outline: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="tile a">
A
</div>
<div class="tile b">
B
</div>
<div class="tile c">
C
</div>
Any tips and feedback are appreciated, even if it takes the code in a completely different direction!
You should handle all direction key events like the following:
$(document).ready(function($) {
var activeIndex = -1;
$(".tile").hover(
function() {
$(this).css({
"outline": "3px solid red"
});
},
function() {
$(this).css({
"outline": "1px solid red"
});
}
);
$("body").keydown(function(event) {
if (event.which == 39) {
activeIndex = 0;
} else if (event.which == 38 && activeIndex != -1) {
activeIndex--;
} else if (event.which == 40 && activeIndex != -1) {
activeIndex++;
}
if (activeIndex < 0) {
activeIndex = 0;
} else if (activeIndex == $("#menu-container").children(".tile").length) {
activeIndex = $("#menu-container").children(".tile").length - 1;
}
if (activeIndex != -1) {
$("#menu-container").children(".tile").css({
"outline": "1px solid red"
});
$("#menu-container").children(".tile").eq(activeIndex).css({
"outline": "3px solid red"
});
}
});
});
.tile {
width: 100px;
height: 100px;
outline: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="menu-container">
<div class="tile a">
A
</div>
<div class="tile b">
B
</div>
<div class="tile c">
C
</div>
</div>
When arrow is pressed you can check for currently active menu item, remove its highlited styles and add them to the next item.
Like this:
if (event.which == 39) {
var active_title = $('.tile.active');
active_title.removeClass('active');
if ( active_title.length && active_title.next().length) {
active_title.next().addClass('active');
} else {
$('.a').addClass('active');
}
}
Try it https://jsfiddle.net/1kf34rdq/11/
Some flag needed
$(document).ready(function($) {
var arrowPressed = false;
$("body").keydown(function(event){
if (event.which == 39) {
if( !arrowPressed ) {
$(".a").css({"outline":"3px solid red"});
arrowPressed = true;
}
else {
$(".a").css({"outline":"none"});
arrowPressed = false;
}
}
});
});

Trouble opening and closing a div with jQuery

.box-one {
border: 0.1em solid #ccc;
}
.dropdown-info {
display: none;
}
<div class="box-one">
<div class="header">
<h3 class="text-center">Sample Header</h3>
</div>
<div class="dropdown-info">
<p class="text-center">Sample Text</p>
</div>
</div>
I'm trying to open and close a div if another div is clicked on and I tried with both .toggle() and .click() but it won't work. I would like to get someone else's opinion on it. I will show how I tried accomplishing it using both methods
$(document).ready(function() {
var descriptionOpen = false;
if (descriptionOpen === false) {
$('.header').click(function() {
$('.dropdown-info').show();
descriptionOpen === true;
});
};
else if (descriptionOpen === true) {
$('.header').click(function() {
$('.dropdown-info').hide();
descriptionOpen === false;
});
};
});
$(document).ready(function() {
$('.header').toggle(function() {
('.dropdown-info').show();
}, function() {
('.dropdown-info').hide();
});
});
Just do this:
$('.header').click(function() {
$('.dropdown-info').toggle();
});

Categories