Undo ( ctrl + z ) functionality to bring back hidden divs - javascript

fiddle http://jsfiddle.net/Q8F5u/3/
I have multiple divs, each having a delete button on its top to delete that particular div(actually i have to hide not delete). After the divs have been deleted I want to retrieve them back by pressing CTRL + Z.
I have had some success in bringing them back. The Logic i have used is that i am pushing the deleted divs id's to a stack and whenever i am pressing ctrl + z, I am popping the last hidden div ID from the stack and using this id to bring back the hidden div.
Here the javascript:
var deletedBlocks = [];
$('.delete').on('click',function(){
var deletedid = $(this).closest('div[id^=block]').attr('id');
deletedBlocks.push(deletedid);
$(this).closest('div[id^=block]').fadeOut(500);
});
$('body').on('keydown',function(e){
//check for ctrl + z key
if( e.ctrlKey && e.which == 90){
if(deletedBlocks.length > 0){
var lastdeleted = deletedBlocks.pop();
$('.container').children('#'+ lastdeleted).fadeIn(1000);
}
else{
alert('NO further Shift to be retrieved');
}
}
});
The problem i am having is that in my real application there's no such unique id's for these divs. instead they all have same classes. How can i implement the same functionality without the div's having unique id's.

If you are just hiding the divs you can actually push the div itself to the stack instead of an ID.
$('.delete').on('click',function(){
var deleted = $(this).closest('div[id^=block]');
deletedBlocks.push(deleted);
deleted.fadeOut(500);
});
$('body').on('keydown',function(e){
//check for ctrl + z key
if( e.ctrlKey && e.which == 90){
if(deletedBlocks.length > 0){
var lastdeleted = deletedBlocks.pop();
lastdeleted.fadeIn(1000);
}else{
alert('NO further Shift to be retrieved');
}
}
});
Note, I just refactored your code. Didn't test it. But you get the idea and it should work.

You might use jQuery's .eq() and .index() methods.
In your line:
var deletedid = $(this).closest('div[id^=block]').attr('id');
You might retrieve the index of the deleted element:
var deletedIndex = $(this).closest('div[id^=block]').index();
Store this index in your array and use .eq() later to undelete the correct element:
$('.container').closest('div[id^=block]').eq(lastdeleted).fadeIn(1000);

Related

Modifying Selectize.js library to allow editing of selected options

Using Selectize.js in an Angular 9 application for selecting multiple values. Please see links to my UI at the end
https://selectize.github.io/selectize.js/
https://github.com/selectize/selectize.js
I'm trying enable the user to edit the already selected values by simply clicking on the selected item. Selectize has the concept of Plugins by which "features can be added to Selectize without modifying the main library." I'm making use of this concept to override onMouseDown event, where I'm attempting to make the clicked item editable. I have successfully used this method to override onKeyDown to implement editing of the last selected value by clicking on backspace. Please see code pasted at the bottom. this.onKeyDown = (function() {...
https://github.com/selectize/selectize.js/blob/master/docs/plugins.md
The already selected items are shown as a layer of div elements over the underlying input. To make a selected item editable, I'm removing the selected element div from the DOM, populating the underlying input element with the text from the div. That way that particular item becomes a input from a div and is editable.
There are a few issues im running into:
Its not possible to determine the caret position from the div that was clicked. I am able to get the div text and pre-populate the input element but not put the caret at the right place in input. By default the caret shows at the end and the user can move it around.
Corer cases around when a name is already being edited and the user clicks on another item to edit. The selectize library is giving api to insert selections only at the end of the already selected items. For me to keep deleting the div's and populating the input to mimic the editing effect I need to be able to insert at different positions but the library doesnt seem to have the capability for it.
Trying to see if anyone has worked on something similar or has any suggestions. Thanks in advance!
var Selectize = require('./selectize-standalone');
(function () {
Selectize.define('break_on_backspace_custom_plugin', function(options) {
var self = this;
options.text = options.text || function(option) {
return option[this.settings.labelField];
};
this.onMouseDown = (function() {
var original = self.onMouseDown;
return function(e) {
var index, option;
if (!this.$control_input.val().length && this.$activeItems.length > 0) {
index = this.caretPos - 1;
var toBeEdited = this.$activeItems[0];
var toBeEditedText = toBeEdited.textContent;
var text = toBeEditedText.substring(0, toBeEditedText.length - 1);
var prevEdit = localStorage.getItem("currentEdit");
if (index >= 0 && index < this.items.length) {
if (this.deleteSelection(e)) {
localStorage.setItem("currentEdit", text);
this.setTextboxValue(text);
this.refreshOptions(true);
if (prevEdit && prevEdit !== text) {
this.addItem(prevEdit);
}
}
//e.preventDefault();
//return;
}
}
//e.preventDefault();
return original.apply(this, arguments);
};
})();
this.onKeyDown = (function() {
var original = self.onKeyDown;
return function(e) {
var index, option;
if (e.keyCode === 8 && this.$control_input.val() === '' && !this.$activeItems.length) {
index = this.caretPos - 1;
if (index >= 0 && index < this.items.length) {
option = this.options[this.items[index]];
if (this.deleteSelection(e)) {
//option.value = option.value.substring(0, option.value.length - 1);
this.setTextboxValue(options.text.apply(this, [option]));
this.refreshOptions(true);
}
e.preventDefault();
return;
}
}
return original.apply(this, arguments);
};
})();
});
return Selectize;
})();
Pictures of UI and work in progress
Editing last element by clicking backspace
https://i.stack.imgur.com/wULcT.png
Editing middle element by clicking on it
https://i.stack.imgur.com/U5hxd.png

How to change ctrl+del behaviour in javascript

I have to restrict user from deleting such words that are in a particular HTML tag (consider I have a custom tag) in a textarea.
<div>
You can Delete Me
<t>
DontDeleteMe
</t>
</div>
The words which are not in a t tag can be deleted.
I tried few logics nothing helps.
Is there any possibilities to get the selection range of ctrl+del keywords do?
case 46:
{ //DEL
if (range.startOffset == startNode.getLength()) {
var ancestor = endNode.$;
while (ancestor != null) {
var next = ancestor.nextSibling;
if (next != null) {
console.log("Next = " + next);
var node = new CKEDITOR.dom.node(next);
cancelEvent = node.isReadOnly();
break;
}
ancestor = ancestor.parentNode;
}
}
break;
}
Unfortunately, your description is quite poor when it comes to describing exactly what you need with examples so nobody really knows what you want to happen to the text.
Here's my Fiddle I just made.
It notes selected text and the ID of the first highlighted text container.
Select the text and press CTRL + Del.
If you use a text area, you can just let the user delete it but then copy it inside again. But why use a text area if the user shouldnt interfere with it? Did i misunderstand the feature you want?
$(document).on('keydown', function(evt) {
if (evt.ctrlKey && evt.keyCode == 46) { // Ctrl + Del key pressed
if (evt.target.id == 'customTextArea') { // Custom tag selected
$('#customTextArea').text('Don't delete me!'); // Copy old text in text area
}
}
});

Javascript - Dynamic Expand/Collapse All

I have a jQuery Tree Report that I am trying to create 'expand/collapse all' buttons for.
The following two pieces of code are fired when the corresponding buttons are pressed and work great:
for (i = 1; i < 100; i++) {
var el = $('#dtt_2597807651112537_table tbody tr')[i - 1];
// store current level
var level = Number($(el).attr('dtt_level'));
// change icon
$(el).find('span.dtt_icon').removeClass('dtt_collapsed_span');
$(el).find('span.dtt_icon').addClass('dtt_expanded_span');
while ($($(el).next()).attr('dtt_level') != null) {
var el = $(el).next();
if ($(el).attr('dtt_level') == (level + 1)) {
// change display
el.removeClass('dtt_collapsed_tr');
el.addClass('dtt_expanded_tr');
} else if ($(el).attr('dtt_level') == level) {
break;
}
}
}
for (i = 1; i < 100; i++) {
// get related table row
var el = $('#dtt_2597807651112537_table tbody tr')[i - 1];
// store current level
var level = Number($(el).attr('dtt_level'));
// change icon
$(el).find('span.dtt_icon').addClass('dtt_collapsed_span');
$(el).find('span.dtt_icon').removeClass('dtt_expanded_span');
while ($($(el).next()).attr('dtt_level') != null) {
var el = $(el).next();
if ($(el).attr('dtt_level') > level) {
// change display
el.addClass('dtt_collapsed_tr');
el.removeClass('dtt_expanded_tr');
// change icon
$(el).find('span.dtt_icon').addClass('dtt_collapsed_span');
$(el).find('span.dtt_icon').removeClass('dtt_expanded_span');
} else if ($(el).attr('dtt_level') == level) {
break;
}
}
};
However, I was wondering if anyone had a nice way to:
1) Get the number of rows that need to be looped through - I just put 100 as a large number to prove my code worked and I don't want to just increase this to an even larger number.
2) Get the class name from the page source - The large number in "dtt_2597807651112537_table" is a report ID generated by the application. This is static for now but I want to eliminate any problems if it changes.
Thanks.
This is all wrong. Well, it's working against how jQuery works, in any case.
jQuery's credo is:
Select elements
Do stuff to them
Drop your loops. You don't need them.
For example. To toggle the icon on all span.dtt_icon in your document, do
var collapsed = true;
$("#dtt_2597807651112537_table span.dtt_icon") // select elements
.toggleClass('dtt_collapsed_span', collapsed) // do stuff to them
.toggleClass('dtt_expanded_span', !collapsed);
or, as a function that can both collapse and expand:
function toggleTree(tree, collapsed) {
$(tree).find("span.dtt_icon")
.toggleClass('dtt_collapsed_span', collapsed)
.toggleClass('dtt_expanded_span', !collapsed);
}
To collapse only the currently expanded ones...
$("#dtt_2597807651112537_table span.dtt_icon.dtt_expanded_span")
.toggleClass('dtt_collapsed_span', true)
.toggleClass('dtt_expanded_span', false);
and so on.
You can boil down your entire code into a few lines that way, and you don't need to write a single loop: Use smart element selection (via jQuery selectors and any of jQuerys find, filter and traversal functions) to single out the elements you want to manipulate and then manipulate them all at once in a single step.
To your second question. There are many ways, pick one:
use known page structure to determine the right table (e.g. $("div.main > table:first") or something to that effect)
use known table contents to determine the right table (e.g. $("table:has(span.dtt_icon)"))
use the table's other classes ($("table.treeReport") maybe?) or for example the table's ID with and a "starts-with" selector ($("table[id^=dtt_]")).
Again it's all about selecting your elements smartly. A dive into the jQuery API documentation, in this case the part about selectors, is recommended.

Global counter variable

im have a code that add and remove dynamically form controls. The add and delete methods works fine. But i like if exist only one control, not remove it.
I defined the next var, outside $(document).ready scope:
var Alumnos = {};
And initialize inside of $(document).ready:
// Valor inicial de casilleros renderizados.
Alumnos.count = 3;
The method that remove controls is:
// Elimina un bloque
$(document).on('click','.closable',function(){
if(Alumnos.count > 1){
var idRow = $(this).attr('data-toggle');
var victim = $(idRow + " .row-fluid:last-child");
victim.remove();
var childs = $(idRow).children();
if(childs.length === 0)
{
$(idRow).remove();
$(this).remove();
Alumnos.count -= 1;
}
}
console.log(Alumnos.count);
return false;
});
The Alumnos.count value persist after delete. Any ideas ?
UPDATE 1
When the user click on "Add more", the code, create a form row with 3 controls, from a prototype.
Because, i cant use children count.
I need the user dont remove all controls.
I think you should have:
if(Alumnos.count >= 1){ //Probably if there is only 1, it's erasable. You had >
And move the decrement outside, like this:
var childs = $(idRow).children();
if(childs.length === 0)
{
$(idRow).remove();
$(this).remove();
}
Alumnos.count -= 1; //This was moved
Hope this helps. Cheers
Thanks for all your replies !! I found the way to control the elements remove with the childs count. May be isnt the best code, but works.
$(document).on('click','.closable',function(){
// Get the id of the row.
var dataToggle = $(this).attr('data-toggle');
// Get the row
var row = $(dataToggle);
// if isnt first row (#id0), ok, remove all if you want.
// if is first row (#id0) and have more than one child, happy remove ^^
if((dataToggle === "#id0" && row.children().length > 1) || dataToggle !== "#id0"){
// remove code...
}
return false;
});

JQuery to dynamically assigning id to dynamically generated div, inside contenteditable div

<div class="pad" id="test" contenteditable="true" spellcheck="false">
<div id="a0" class="lines" > text1</div>
</div>
I have a content editable div, and many child divs inside it which gets generated dynamically when user presses ENTER key (using jQuery ) to go to next line .An example of child div is the one above with id='a0'.
Aim: To have a unique id be dynamically generated and assigned to these dynamically generated child divs.
Below is the jQuery which I have used to to accomplish this task.
function getSelectedNode()
This is thefunction which returns the 'id' of line (i.e. child div) on which user is currently typing.
var lineCount=0;
lineCount variable has the value equal to total number of child divs generated.
$(currentLine).next().attr('id','a'+(++lineCount));
Here currentLinecontains id of the line on which user is currently typing.On pressing ENTER key new div gets automatically generated after the current div and I try to access using .next() and try to assign a new id to it using .attr('id','a'+(++lineCount)).
Process of assign new id works well if I pass absolute value in the selector clause of jQuery i.e.:
$('#a0').next().attr('id','a'+(++lineCount));
instead of
$(currentLine).next().attr('id','a'+(++lineCount));
var lineCount = 0;
var currentLine = "#a0";
function getSelectedNode() {
if (document.selection)
return document.selection.createRange().parentElement();
else {
var selection = window.getSelection();
if (selection.rangeCount > 0)
return selection.getRangeAt(0).startContainer.parentNode;
}
}
$('.pad').keypress(function (event) {
var keycode = (event.keyCode ? event.keyCode : event.which);
if (keycode == '13') {
var sn = getSelectedNode();
currentLine = "#" + sn.getAttribute('id');
$(currentLine).next().attr('id', 'a' + (++lineCount));
}
});
Javascript is single threaded, so you can use an int for this...
var indexer = 0;
function getUniqueId(){
indexer++;
return indexer.toString();
}
Caveat: I don't recommend declaring functions and properties at the document level in most web applications. Once you have this method tho, it's easy to implement...
$("<div id='myDiv" + getUniqueId() + "' />");

Categories