FabricJS Delete multiple object in V.2 - javascript

I am trying to select multiple and delete. Selecting one object deletes just fine but when selecting multiple it doesn't do anything. I have looked around and seen this answer with a fiddle which works in v1.4
https://stackoverflow.com/a/41286840
When I select multiple I get
canvas.getActiveGroup is not a function
Now as the getActiveObject can go from an array. I tried to check if greater than one using length and then deleting those objects but it always goes through the
if (activeObject) {
instead of
else if (activeObject.length >= 2) {
But neither will work. Doesn't Fabric have a function with multiple selected items?

As it is mentioned on change log getActiveGroup this function is removed now.
So you need to get the object using canvas.getActiveObjects() then loop through the objects present and remove them from canvas.
Here is jsFiddle

canvas.getActiveGroup was removed in the new versions. so here's the code work fine. try this. more information about fabricJs changes click to refer this fabricJS breaking changes
deleteSelectedObject() {
let activeObject = this.canvas.getActiveObjects();
if (activeObject) {
let objectsInGroup = activeObject;
this.canvas.discardActiveObject();
let self = this;
objectsInGroup.forEach(function(object) {
self.canvas.remove(object);
});
}
}

Related

Reset or Clear before appending an element in javascript

It's my first app, I have been in tutorial hell for one year and decided to start my own little app.
So in this part Im using an addEventListener(),which appends and create text. The text is a variable created by the sum of the results of other functions (I don't know if this is relevant), but the thing is once I submit a button and creates the HTML element, if I keep submitting the elements will pile up. So I tried removeChild() but it didn't work because the variable was already created with its value, or...brought an error that can't removedChild() of undefined (since I tried to clear the values before appending), also tried to reseting the variable by adding a .innerHTML=""; but I don't know where to locate it. I also checked the replaceChild() but it didn't make sense, since everytime you click should be a new first event created. I tried the empty().append() but seems it's for jquery.
Seems that I need to learn a lot about scopes.
let frase;
function armarPersonaje() {
if(!reversarNombre() || !descMes || !descDia){
return false
} else {
frase = nombreReves + descMes + descDia;
return true
}
}
let div;
let h3Element;
function mostrarPersonaje(){
let div = document.getElementById('container')
let h3Element = document.createElement("h3")
h3Element.className = "addedH3"
if(frase.length > 0){
h3Element.appendChild(document.createTextNode(frase));
div.appendChild(h3Element)
}
}
enter image description here
Here's the complete code
https://jsfiddle.net/santiso/mzv3ct5e/
The problem here that in your last function mostrarPersonaje() you are always creating the element h3Element before the if , and even if you empty the parent node which is the div it will create a new element whit class addedH3 the very next click !
How to fix it ? first do not create until you enter the condition , then delete it when ever you click again even before the condition
here is the fiddle
and this is what i added
let oldH3Element = document.querySelector('h3.addedH3')
if(oldH3Element) oldH3Element.parentNode.removeChild(oldH3Element)

Utilizing .each() with an array

I am creating a Matching Card game using jQuery. Currently, I am running into an issue where the playerChoices array in my code does not update the 'matched' cards with the 'matched' class.
var playerChoices = [];
function showCard(){
$(this).addClass('selected'); //mark the selection with the selected class
playerChoices.push($(this)); //push the players choice onto the playerChoices array
console.log(playerChoices);
moves++;
console.log(moves);
$('#buttons').show();
matchCards(playerChoices);
}
Here is the function in question where the issues lie:
function matchCards(array){
if(playerChoices.length === 3){
//when the player finds the first match
if(playerChoices[0].attr('class') === playerChoices[1].attr('class')){ //if both playerChoices have the class
console.log("match found at index 0 and 1 of playerchoice!");
**$(playerChoices).each(playerChoices, function(index, element){
$(this).addClass('matched');**
})
}
//Deselect the two choices that were made
else{
$(playerChoices).each(function(index, element){
$(this).removeClass('selected');
})
playerChoices = [];
}
}
else if(playerChoices.length === 4){
//when the player gets the second match
if(playerChoices[2].attr('class') === playerChoices[3].attr('class')){
console.log("match found at index 2 and 3 of playerchoice!");
**$(playerChoices).each(playerChoices, function(index, element){
$(this).addClass('matched');**
})
**showGameOverMessage();**
}
//Deselect the last two choices that were made
else{
$(playerChoices).each(function(index, element){
$(this).removeClass('selected');
})
}
}
}
The primary issue here are the area's that I have 'asterisks' around in my code. I set up break points in the console, and I found that the code was never reaching the $(this).addClass('matched') lines. I've never used .each before and have looked at the examples api.jquery.com but I still was not able to overcome this issue of applying the matched class to my 'matched' cards.
FYI: I tried to get my code to work in JSFiddle but I kept getting errors with the images of my cards. My code works outside of that, I am just not able to get the matching class to apply appropriately.
https://jsfiddle.net/2sharkp/54s47vzb/ Works now
Any help is greatly appreciated!
Your updated question makes the problem clear: You're pushing jQuery instances into playerChoices:
playerChoices.push($(this));
...then later using $(playerChoices).each(...) to try to loop over them. While $() accepts arrays of HTML elements in the $() function, it doesn't correctly understand it if you pass it an array of jQuery instances — you end up with a jQuery instance wrapped around that set of jQuery instances, which isn't useful — you may as well just use the array (or use a single jQuery instance as I describe later).
You can use $.each (the one on the jQuery function itself):
$.each(playerChoices, function() {
// ...`this` (not `$(this)`) here will be a jQuery instance:
this.addClass('matched');
});
Updated Fiddle
But you really dont need $.each, just use the array's built-in forEach:
playerChoices.forEach(function(entry) {
// ...`entry` here will be a jQuery instance
entry.addClass('matched');
});
Updated Fiddle
...or there are lots of other ways to loop through arrays outlined in my answer here.
That said, you might consider making playerChoices a (single) jQuery instance. jQuery is set-based, so a single jQuery instance can contain multiple HTML elements that you can then act on with just a single method call. For instance, if you made playerChoices a jQuery instance, instead of:
playerChoices.forEach(function(entry) {
entry.addClass('matched');
});
You could do this:
playerChoices.addClass('matched');
To do that, start with:
playerChoices = $();
...and add elements via add:
playerChoices.add(this);
Try removing playerChoices before callback
$(playerChoices).each(function(index, element){
$(this).addClass('matched');
})
jsfiddle https://jsfiddle.net/xowkyh6p/1/

How to update the source Option in bootstrap-typeahead.js

I am using bootstrap-typeahead in order to allow multiple selection.
Here is the demo.
The original code has been update by #Sherbrow Twitter bootstrap typeahead multiple values
My question is related to the following use case:
after inserting Alaska value, I would like to update the source not showing again Alaska value.
Any hints?
I had the same problem and this one will save you a lot of time. I've updated your old jsFiddle with my code example. The basic thing is that you need to do
var autocomplete = $('input').typeahead();
autocomplete.data('typeahead').source = newSource;
Where newSource is the new array. Now you just need a function that adds or removes an element, or whatever you need to do with it.
None of the given answers worked for me, I had to destroy the original typeahead instance and re initialize it.
$('input').typeahead('destroy').typeahead(options);
Based on the default updater method of typeahead :
updater: function (item) {
var pos = this.source.indexOf(item);
if(pos != -1) {
var newSource =
this.source.slice(0,pos)
.concat(this.source.slice(pos+1));
this.source = newSource;
}
return item
}
Demo with multiple values (jsfiddle)
Keep in mind that you can access this source from anywhere with $('sel').data('typeahead').source considering that the typeahead is initialized

Javascript: Get arrays based on dropdown selection

ok. I have a dropdown. well call it dropdownA. I have several arrays already fashioned, that i want to loop through, and put each individual value out onto the page into it's own input field, based on the selection in the dropdown. I know how to put together the loop to get the values out onto the page. anyway, here is the meta code.
meta1array=new Array('','','','','','');
meta2array=new Array('','','','','','');
meta3array=new Array('','','','','','');
function metacode(id){
{
var a=get.the.dd.selIndex;
var b=get.the.dd.options[a].value; //comes back as 'meta1'. other opts are meta2, meta3
var c=b+'array';
for(i=0;i<c.count;i++)
{
loop here to popout values
}
}
i have looked everywhere, and i haven't come up with anything. I also admit that my brain is starting to mushify from a few weeks straight of coding, and this is the first time i have come across this. so, please. i would be greatful for any help.
Global variables are members of the window object.
var c = window[b + 'array'];
It might be wise to make your arrays members of some other object though, for tighter scoping (avoid cluttering the global namespace).
metaobject = {
meta1array: ['','','','',''],
meta2array: ['','','','',''],
meta3array: ['','','','','']
};
// snip
var arr = metaobject[b + 'array'];
for(var i=0;i<arr.length;i++) {
//do work
}
Check out the fiddle using jquery here.

Removing items from data bound array

How do I remove an items from a data bound array? My code follows.
for(var i = 0; i < listBox.selectedIndices.length; i++) {
var toRemove = listFiles.selectedIndices[i];
dataArray.splice(toRemove, 1);
}
Thanks in advance!
Edit Here is my swf. The Add Photos works except when you remove items.
http://www.3rdshooter.com/Content/Flash/PhotoUploader.html
Add 3 photos different.
Remove 2nd photo.
Add a different photo.
SWF adds the 2nd photo to the end.
Any ideas on why it would be doing this?
Edit 2 Here is my code
private function OnSelectFileRefList(e:Event):void
{
Alert.show('addstart:' + arrayQueue.length);
for each (var f:FileReference in fileRefList.fileList)
{
var lid:ListItemData = new ListItemData();
lid.fileRef = f;
arrayQueue[arrayQueue.length]=lid;
}
Alert.show('addcomplete:' + arrayQueue.length);
listFiles.executeBindings();
Alert.show(ListItemData(arrayQueue[arrayQueue.length-1]).fileRef.name);
PushStatus('Added ' + fileRefList.fileList.length.toString() + ' photo(s) to queue!');
fileRefList.fileList.length = 0;
buttonUpload.enabled = (arrayQueue.length > 0);
}
private function OnButtonRemoveClicked(e:Event):void
{
for(var i:Number = 0; i < listFiles.selectedIndices.length; i++) {
var toRemove:Number = listFiles.selectedIndices[i];
//Alert.show(toRemove.toString());
arrayQueue.splice(toRemove, 1);
}
listFiles.executeBindings();
Alert.show('removecomplete:' + arrayQueue.length);
PushStatus('Removed photos from queue.');
buttonRemove.enabled = (listFiles.selectedItems.length > 0);
buttonUpload.enabled = (arrayQueue.length > 0);
}
It would definitely be helpful to know two things:
Which version of ActionScript are you targeting?
Judging from the behavior of your application, the error isn't occurring when the user removes an item from the list of files to upload. Looks more like an issue with your logic when a user adds a new item to the list. Any chance you could post that code as well?
UPDATE:
Instead of: arrayQueue[arrayQueue.length]=lid
Try: arrayQueue.push(lid)
That will add a new item to the end of the array and push the item in to that spot.
UPDATE 2:
Ok, did a little more digging. Turns out that the fileList doesn't get cleared every time the dialog is opened (if you're not creating a new instance of the FileReferenceList each time the user selects new files). You need to call splice() on the fileList after you add each file to your Array.
Try something like this in your AddFile() method...
for(var j:int=0; j < fileRefList.fileList.length; j++)
{
arrayQueue.push(fileRefList.fileList[j]);
fileRefList.fileList.splice(j, 1);
}
That will keep the fileList up to date rather than holding on to previous selections.
I see one issue. The selected indices are no longer valid once you have spliced out the first element from the array. But that should only be a problem when removing multiple items at once.
I think we need to see more code about how you are handling the upload before we can figure out what is going on. It looks to me like you are holding a reference to the removed FileReference or something. The described problem is occurring when you upload a new file, not when you remove the selected one.
Do you mean to use listBox and listFiles to refer to the same thing?
I'm stepping out on a limb here, because I don't have a ton of experience with JavaScript, but I'd do this the same way that I'd do it in C, C++, or Java: By copying the remaining array elements down into their new locations.
Assuming that listFiles.selectedIndices is sorted (and its contents are valid indices for dataArray), the code would be something like the following:
(WARNING: untested code follows.)
// Don't bother copying any elements below the first selected element.
var writeIndex = listFiles.selectedIndices[0];
var readIndex = listFiles.selectedIndices[0] + 1;
var selectionIndex = 1;
while(writeIndex < (dataArray.length - listFiles.selectedIndices.length)) {
if (selectionIndex < listFiles.selectedIndices.length) {
// If the read pointer is currently at a selected element,
// then bump it up until it's past selected range.
while(selectionIndex < listFiles.selectedIndices.length &&
readIndex == listFiles.selectedIndices[selectionIndex]) {
selectionIndex++;
readIndex++;
}
}
dataArray[writeIndex++] = dataArray[readIndex++];
}
// Remove the tail of the dataArray
if (writeIndex < dataArray.length) {
dataArray.splice(writeIndex, dataArray.length - writeIndex);
}
EDIT 2009/04/04: Your Remove algorithm still suffers from the flaw that as you remove items in listFiles.selectedIndices, you break the correspondence between the indices in arrayQueue and those in listFiles.selectedIndices.
To see this, try adding 3 files, then doing "Select All" and then hit Remove. It will start by removing the 1st file in the list (index 0). Now what had been the 2nd and 3rd files in the list are at indices 0 and 1. The next value taken from listFiles.selectedIndices is 1 -- but now, what had been the 3rd file is at index 1. So the former File #3 gets spliced out of the array, leaving the former 2nd file un-removed and at index 0. (Using more files, you'll see that this implementation only removes every other file in the array.)
This is why my JavaScript code (above) uses a readIndex and a writeIndex to copy the entries in the array, skipping the readIndex over the indices that are to be deleted. This algorithm avoids the problem of losing correspondence between the array indices. (It does need to be coded carefully to guard against various edge conditions.) I tried some JavaScript code similar to what I wrote above; it worked for me.
I suspect that the problem in your original test case (removing the 2nd file, then adding another) is analogous. Since you've only shown part of your code, I can't tell whether the array indices and the data in listFiles.selectedIndices, arrayQueue, and fileRefList.fileList are always going to match up appropriately. (But I suspect that the problem is that they don't.)
BTW, even if you fix the problem with using splice() by adjusting the array index values appropriately, it's still an O(N2) algorithm in the general case. The array copy algorithm is O(N).
I'd really need to see the whole class to provide a difinitive answer, but I would write a method to handle removing multiple objects from the dataProvider and perhaps assigning a new array as the dataProvider for the list instead of toying with binding and using the same list for the duration. Like I said, this is probably inefficient, and would require a look at the context of the question, but that is what I would do 9unless you have a big need for binding in this circumstance)
/**
* Returns a new Array with the selected objects removed
*/
private function removeSelected(selectedItems:Array):Array
{
var returnArray:Array = []
for each(var object:Object in this.arrayQueue)
{
if( selectedItems.indexOf(object)==-1 )
returnArray.push( object )
}
return returnArray;
}
You might be interested in this blog entry about the fact that robust iterators are missing in the Java language.
The programming language, you mentioned Javascript, is not the issue, it's the concept of robust iterators that I wanted to point out (the paper actually is about C++ as the programming language).
The [research document]() about providing robust iterators for the ET++ C++ framework may still e helpful in solving your problem. I am sure the document can provide you with the necessary ideas how to approach your problem.

Categories