The following code works, but I think there's room for improvement. The index check is there because after the first element is removed the next element looks like it has an index of -1, but is actually the previously removed element. Then it iterates again and finds the clicked element and removes it. BUT since the index is -1 on the first go around the wrong group gets deleted.
How do I keep the zombie elements from being iterated on more efficiently? This is in a backbone view with an in page confirmation.Thanks.
EDIT: To add HTML
Group section always has a default group that shouldn't be deleted.
<div class="section-border-top--grey js-favorite-group">
<h4 class="expandable__cta cta--std-teal js-expand-trigger"><span class="icon icon-plus--teal expandable__cta-icon"></span>All Doctors</h4>
<div class="expandable__content js-favorite-doctor-row-container" aria-expanded="true">
<div class="location-section dr-profile">
<div class="section__content js-doctor-row">
<div class="favorite-doctor-manage__row">
DR info
</div>
</div><!--/section__content-->
</div><!--/location-section-->
</div><!--/expandable__content-->
Tag section to remove groups
<div class="js-favorite-doctor-manage-add-remove">
<div class="grid-construct">
<div class="expandable" data-controller="expandable">
<ul class="tag-list js-group-list" tabindex="-1">
<li class="tag tag--teal" >
Lauren's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Lauren's Doctors</button></li>
</ul>
</li>
<li class="tag tag--teal" >
Timmy's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Timmy's Doctors</button></li>
</ul>
</li>
</ul>
</div>
removeGroup: function( evt ) {
var deleteGroup = function() {
if ( $(evt.currentTarget).closest('.tag').hasClass('is-active')){
var clickedTag = $(evt.currentTarget).closest('.tag');
var groupList = this.$el.find('.js-group-list');
var groupTags = groupList.find('.tag');
var index = groupTags.index(clickedTag);
var groupSections = $('.js-favorite-group');
// add one to account for "All" section which is never removed
var groupToRemove = groupSections.eq(index + 1);
console.log(groupToRemove);
var removedGroupName = this.getGroupNameForSection(groupToRemove);
var allDoctors = groupSections.eq(0);
var allDoctorsContainer = allDoctors.find('.js-favorite-doctor-row-container');
if ( index > -1 ){
groupToRemove.find('.js-favorite-doctor-row').appendTo(allDoctorsContainer);
clickedTag.remove();
groupToRemove.remove();
this.updateSectionDropdowns();
this.ariaAlert('Group ' + removedGroupName + ' removed');
this.hideConfirm(evt);
}
}
};
this.showAlert(evt, deleteGroup);
},
showAlert: function (evt, callback) {
that = this;
var clickedTag = '';
clickedTag = $(evt.currentTarget).closest('.tag');
clickedTag.addClass('is-active').attr('data-delete','true');
$('.delete-acct-message').show().focus();
$('.js-remove-yes').on('click', function(evt){
evt.preventDefault();
callback.apply(that);
});
$('.js-remove-no').on('click', function(evt){
evt.preventDefault();
this.hideConfirm(evt);
});
},
I would suggest that you should use custom attributes in your html, this will simplify your javascript logic and make it more effective and efficient.
I have modified your html and javascript to add the support for custom attribute data-doc-group. Have a look at your group sections div here
<div data-doc-group="lauren" class="section-border-top--grey js-favorite-group">
<h4 class="expandable__cta cta--std-teal js-expand-trigger"><span class="icon icon-plus--teal expandable__cta-icon"></span>Lauren's Doctors</h4>
<div class="expandable__content js-favorite-doctor-row-container" aria-expanded="true">
<div class="location-section dr-profile">
<div class="section__content js-doctor-row">
<div class="favorite-doctor-manage__row">
DR info
</div>
</div><!--/section__content-->
</div><!--/location-section-->
</div>
Here are the tags with custom attributes
<li data-doc-group="lauren" class="tag tag--teal">
Lauren's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Lauren's Doctors</button></li>
</ul>
</li>
<li data-doc-group="timmy" class="tag tag--teal">
Timmy's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Timmy's Doctors</button></li>
</ul>
</li>
Here is the javascript to handle this, (this may be a bit buggy, but will give you a general idea)
removeGroup: function(evt) {
this.showAlert(evt, function() {
var $clickedTag = $(evt.currentTarget).closest('.tag'),
dataGroupName,
$groupToRemove,
removedGroupName,
$allDoctors = $('.js-favorite-group').eq(0),
$allDoctorsContainer = $allDoctors.find('.js-favorite-doctor-row-container');
if ($clickedTag.hasClass('is-active')){
dataGroupName = $clickedTag.data('doc-group');
$groupToRemove = $allDoctors.siblings('[data-doc-group="' + docGroupName + '"]');
if ($groupToRemove.length > 0){
$groupToRemove.find('.js-favorite-doctor-row').appendTo($allDoctorsContainer);
$clickedTag.remove();
$groupToRemove.remove();
removedGroupName = this.getGroupNameForSection($groupToRemove);
this.updateSectionDropdowns();
this.ariaAlert('Group ' + removedGroupName + ' removed');
this.hideConfirm(evt);
}
}
});
}
Related
I have below mark-up . I am doing some DOM manipulation.I am trying to do using vanila js instead of jquery .
In jQuery i am writing like this
var $window = $(window),
$container = $('.rc105w1'),
$categories = $('.rc105w2'),
$counts = $('.rc105count'),
$productLists = $('.rc105w3'),
vdub = window.innerWidth,
resizeTimeout;
labelCategories();
function labelCategories() {
var row = 1,
prevoffset = 0,
guid = 0,
offset;
$categories.each(function() {
var $category = $(this),
$productList = $category.find($productLists),
$link = $category.find('> a');
console.log($productList.length,'product length')
console.log($link.length,'link length')
});
}
})
using this line $category.find($productLists) I am checking number of descendent or matching elements
I want to do same thing using vanilla js .I tried like this
function init(){
console.log('javascript code start')
var $window = window,
$container = document.querySelectorAll('.rc105w1'),
$categories = document.querySelectorAll('.rc105w2'),
$counts = document.querySelectorAll('.rc105count'),
$productLists = document.querySelectorAll('.rc105w3'),
vdub = window.innerWidth,
resizeTimeout;
labelCategories();
function labelCategories() {
var row = 1,
prevoffset = 0,
guid = 0,
offset;
$categories.forEach(function(el) {
var $category = el,
$productList = $category.querySelectorAll($productLists),
$link = $category.querySelectorAll('> a');
console.log($productList.length,'javasscript $productList length ')
console.log($link.length ,'javasscript $link length ')
});
}
}
function docReady(fn) {
// see if DOM is already available
if (document.readyState === "complete" || document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
docReady(init)
I am not able to find matching element or descendent elements any way to find using javascript ?
<section class="rc105 rc105v0">
<div class="cwidth">
<div class="rc105w1">
<div class="rc105w2 rc105w2-bttn">
<a href="/cloud/migrate-applications-to--cloud/">
<span> Applications</span>
</a>
</div>
<div class="rc105w2 rc105w2-bttn">
<a href="/cloud/migrate-custom-applications-to-cloud/">
<span>Custom Applications</span>
</a>
</div>
</div>
<div class="rc105w1 rw-scrim-neutral-40bg">
<div class="rc105w2 rc105w2-drawer rc105w2-6up rc105w2-12set rc105w2-tallbttn">
<a href="#developer-services">
<span>Developer Services</span>
</a>
<div class="rc105w3">
<h3>Developer Services</h3>
<p>Build, deploy, and manage modern cloud applications using developer friendly tools and services.</p>
<div class="rc105w4">
<div class="rc105w5 rc105w5-half">
<h4>Build and run</h4>
<ul class="rc105linklist rc105w5-2col">
<li>API Gateway</li>
<li>API Management</li>
</ul>
</div>
<div class="rc105w5 rc105w5-quarter">
<h4>Low code</h4>
<ul class="rc105linklist">
<li>Application Express (APEX)</li>
<li>Visual Builder Studio</li>
</ul>
</div>
<div class="rc105w5 rc105w5-quarter">
<h4>Extend</h4>
<ul class="rc105linklist">
<li>Application Integration</li>
<li>Content Management</li>
</ul>
</div>
</div>
</div>
</div>
<div class="rc105w2 rc105w2-drawer rc105w2-6up rc105w2-12set rc105w2-tallbttn">
<a href="#containers-and-functions">
<span>Containers and Functions</span>
</a>
<div class="rc105w3">
<h3>Containers and Functions</h3>
<p>Deploy microservices applications on high-performance, managed open source Docker, Kubernetes, and Fn Functions services.</p>
<div class="rc105w4">
<div class="rc105w5">
<ul class="rc105linklist">
<li>Functions</li>
<li>Kubernetes Engine</li>
<li>Registry</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
querySelectorAll() expects a selector not a (static) Nodelist.
To make your code work you can just pass the selector like you do with $link.
$productList = $category.querySelectorAll('.rc105w3')
Maybe the problem occurs when you try to use querySelectorAll, because it will provide you an array, so try using querySelector instead of querySelectorAll and also querySelectorAll expects a selector not a Nodelist
So, overall I have a word bank, and I'm trying to allow the user to drag any of the words into several other boxes, except I'm not allowing duplicates in any of the boxes, including if a word is dragged back to the word bank. A. And I'm trying to edit the text to make it smaller once it's dragged into the other boxes, or larger again if it's dragged back into the word box.
Not all of that is working yet.
So I'm starting out:
1) I need to accurately get the id's and classes of all the elements and lists. Right now: If I drag one list item to a different list, how can I access the id of the previous list it came from? My ui.sender[0].id is outputting the id of my word item, not the id of the previous list it belonged to.
2) Additionally, if there are several items in a list, how can I access those ids? I used var element = $(this).sortable("toArray") and it's not accessing them correctly; it comes up as undefined when I have multiple items in each list, and I test via element[0].id, etc.
Here's a portion of what I have in JS:
var origin = null;
$("#wordBank,.words").draggable({
helper: "clone",
connectToSortable: ".sort",
start: function() {
origin = $(this).parent()[0].id;
}
});
$(".words,.sort").sortable({
connectWith: "#wordBank,.sort",
receive: function(event, ui) {
ui.item.toggleClass("highlight");
var currentListID = this.id; //where the item is dropped
var originatedID = origin; //where item came from
var itemContent = ui.item[0].innerHTML; //content in item
var currentListLength = this.children.length;
//alert("Ok");
//alert("currentListID = " + currentListID);
//alert("item content = " + itemContent);
//alert("current list length = " + currentListLength);
//Here I thought I could access all of the elements in a list if
//they had at least two items, but it's not working
var element = $(this).sortable("toArray");
alert("element[0] = " + element[0].id);
}
});
$().disableSelection();
.border {
border: 2px solid green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<ul id="wordBank" class="bank1">
<li id="word1" class="words">Word1</li>
<li id="word2" class="words">Word2</li>
<li id="word3" class="words">Word3</li>
<li id="word4" class="words">Word4</li>
<li id="word5" class="words">Word5</li>
</ul>
<div id="drop1" class="border sort">1. </div>
<div id="drop2" class="border sort">2. </div>
<div id="drop3" class="border sort">3. </div>
<div id="drop4" class="border sort">4. </div>
Well there are many things that need to be fixed before you can try to run your code:
($this) needs to be changed to $(this), in jQuery all objects are called with $(selector).
Instead of using alert() use console.log() for logging or try to debug the code with your browser.
obj is not defined in the code, try to check it was correctly defined before using it.
And to get the source element id of the dragged element, you can use start event of jQuery-ui draggable to track the dragged element's parent id when the drag opertaion is started.
This is the right code:
var origin = null;
$("#wordBank,.words").draggable({
helper: "clone",
connectToSortable: ".sort",
start: function() {
origin = $(this).parent()[0].id;
}
});
Demo:
This is an updated and revised version of your code with the right code to track this id:
var origin = null;
$("#wordBank,.words").draggable({
helper: "clone",
connectToSortable: ".sort",
start: function() {
origin = $(this).parent()[0].id;
}
});
$(".words,.sort").sortable({
connectWith: "#wordBank,.sort",
receive: function(event, ui) {
ui.item.toggleClass("highlight");
var currentListID = this.id;
var originatedID = origin;
var itemContent = ui.item[0].innerHTML;
var currentListLength = this.children.length;
console.log("Ok");
console.log("currentListID = " + currentListID);
console.log("originatedID = " + originatedID);
console.log("item content = " + itemContent);
console.log("current list length = " + currentListLength);
var element = $(this).sortable("toArray");
console.log("element[0] = " + element[0]);
if (currentListID === "#wordBank") {
console.log("removing list-inline-item on " + itemContent);
ui.item[0].classList.remove("list-inline-item");
} else if (!ui.item[0].classList.contains("list-inline-item")) {
ui.item[0].classList.add("list-inline-item");
}
if (originatedID === "wordBank") {
ui.item.css("font-size", "10px");
} else if (currentListID === "wordBank") {
ui.item.css("font-size", "20px");
if (ui.item[0].classList.remove("list-inline-item"));
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<ul id="wordBank" class="bank1">
<li id="word1" class="words">Word1</li>
<li id="word2" class="words">Word2</li>
<li id="word3" class="words">Word3</li>
<li id="word4" class="words">Word4</li>
<li id="word5" class="words">Word5</li>
</ul>
<div id="drop1" class="border sort">1. </div>
<div id="drop2" class="border sort">2. </div>
<div id="drop3" class="border sort">3. </div>
<div id="drop4" class="border sort">4. </div>
My html page is :
<div data-role="content">
<div id="menu">
<ul id="menu" data-role="listview" class="ui-listview "
data-bind="foreach: menu">
<li>
<a data-bind="text:name, attr: {href: urlmenu}"></a>
<a href="#" data-bind="{ click: $parent.remove }"
data-role="button" data-icon="delete"></a>
</li>
</ul>
</div>
</div>
<div data-role="footer" data-position="fixed">
<div data-role="navbar">
<ul id="footer">
<li>Home</li>
<li>Asignaturas</li>
<li>Mensajes</li>
</ul>
</div>
</div>
And my JS code is :
$( document ).on( "pagebeforechange" , function(e, data) {
var toPage = data.toPage[0].id;
if( toPage == "home"){
ko.cleanNode(document.getElementById('menu'));
menu();
}
});
function menuViewModel(){
var self = this;
self.menu = ko.observableArray([]);
self.menu.removeAll();
self.menu = ko.observableArray([
new EditMenuViewModel("Perfil"),
new EditMenuViewModel("Asignaturas")
]);
}
function EditMenuViewModel(name) {
this.name = ko.observable(name);
this.urlmenu = ko.observable("#"+name);
};
function menu(){
var menuviewModel = new menuViewModel();
ko.applyBindings(menuviewModel, document.getElementById('menu'));
}
When I load my page for the first time everything works fine, but when I click on link footer home, the array content is duplicated.
Example is here:
Any idea?
Thanks
You have two DOM elements with id=menu, a div and a ul.
<div id="menu"> <!-- <-- change this id for example -->
<ul id="menu" data-role="listview" class="ui-listview "
data-bind="foreach: menu">
...
</ul>
</div>
Ids should be unique, you need to change the id on one of your elements, hopefully this will also solve your problem.
Update
As you can read in this thread, ko.cleanNode will not remove items created using foreach binding.
You need to change your approach.
Here is a jsFiddle that reproduces your problem.
What you can do is stop cleaning+applying bindings, and update your observableArray instead:
$( document ).on( "pagebeforechange" , function(e, data) {
var toPage = data.toPage[0].id;
if( toPage == "home"){
menuviewModel.menu.removeAll(); //clear menu
//add whatever menu item you need
menuviewModel.menu.push(new EditMenuViewModel("New Menu1 " + (new Date()).getTime()));
menuviewModel.menu.push(new EditMenuViewModel("New Menu2 " + (new Date()).getTime()));
}
});
function menuViewModel(){
var self = this;
self.menu = ko.observableArray([]);
self.menu.removeAll();
self.menu = ko.observableArray([
new EditMenuViewModel("Perfil"),
new EditMenuViewModel("Asignaturas")
]);
}
function EditMenuViewModel(name) {
this.name = ko.observable(name);
this.urlmenu = ko.observable("#"+name);
};
//bind only once
var menuviewModel = new menuViewModel();
ko.applyBindings(menuviewModel, document.getElementById('menu'));
Here is an example
This is old thread, but I've found a (ugly) way to overcome it:
before the cleaning, I'm caching the observable array values and set it with only 1 value. After the rebind, I'm restoring the cached values. Something like this:
var self = this;
self.myArray = ko.observableArray(['val1', 'val2']);
var tempArray = [];
self.BeforeCleaning = function () {
tempArray = self.myArray()
self.myArray(['temp value']);
};
self.AfterRebinding = function () {
self.myArray(tempArray);
};
horrible, but works for me.
This question already has answers here:
Get index of clicked element using pure javascript
(10 answers)
Closed 9 years ago.
I have a set of <li> items:
<ul id="listings">
<li id="item-0">
<div class="content">hi</div>
<li>
<li id="item-1">
<div class="content">hi</div>
<li>
<li id="item-2">
<div class="content">hi</div>
<li>
</ul>
//etc
My question is: How do I determine the index number of the clicked <li> item?.
Like if I click: <li id="item-1"> I want to know it's the second <li> index.
I can easily determine the total lenght of all <li>'s by doing:
document.querySelectorAll('#listings li').length;
And I currently have the following click listener:
document.getElementById("listings").addEventListener("click", function(e) {
if(e.target && e.target.nodeName == "LI") {
console.log(e.target.id + " was clicked");
}
});
<ul id="listings">
<li id="item-0" onclick="isClicked(0)">
<div class="content">hi</div>
</li>
<li id="item-1" onclick="isClicked(1)">
<div class="content">hi</div>
</li>
<li id="item-2" onclick="isClicked(2)">
<div class="content">hi</div>
</li>
//...
I would add an onclick function to the li. Using javascript you can collect the data.
function isClicked(number){
alert("Clicked element: "+ number);
}
var ulLength = document.getElementById('listings');
var elements = [].slice.call(ulLength.childNodes);
//[].slice.call() is used to convert array-like objects/collections into an array
function getSiblings() {
var _s = [];
elements.forEach(function (i, d) {
if (i.nodeType === 1) //prevents text nodes to be counted as siblings
_s.push(i);
});
return _s;
}
function getElmIndex(elm) {
var _siblings = getSiblings();
for (var i = 0; i < _siblings.length; i++) {
if (!(_siblings[i] && _siblings[i] !== elm)) {
return i;
}
}
}
elements.forEach(function (elm, d) {
elm.addEventListener("click", function (e) {
var _index = getElmIndex(this);
alert("Index: " + _index);
});
});
Fiddle Link
If using Jquery is not a problem, I suggest the use of the index() method: http://jsfiddle.net/JLkPs/ .
Source: http://api.jquery.com/index/
Hello i have cart full with Elements
This Ex of one of them
<div class="item-container cart-item">
<div>
<img border="0" onerror="src='http://www.myengravedjewelry.com/Admin/surfing.jpg'" title="Bloody Mary Style Colour Name Necklace" src="http://www.myengravedjewelry.com/Admin/surfing.jpg" alt="1009">
<div class="item-header">
<div class="item-body">
<ul class="item-ul">
<li>
<li>
<li>
<span class="bold-14">Price:14.9 </span>
</li>
<li>
<span>ShortId:1010 </span>
</li>
<li>
<span>LongId:110-01-073-10 </span>
</li>
<li>
<span class="spanDefCat">DefaultCat:334 </span>
</li>
</ul>
</div>
</div>
<div class="item-footer"></div>
</div>
When i press save i go trow each one of this element and check if DefaultCat==0
var elements = document.getElementsByClassName("cart-item");
and i try to get to this defaulCat like this
for(i=0;i<elements.length;i++){
var elementContent=elements[i].find(".spanDefCat").html();
var vars = elementContent.split(" ");
var obj = {};
vars.forEach(function(v) {
var keyValue = v.split(":");
obj[keyValue[0]] = keyValue[1];
});
DefaultCat = obj["DefaultCat"];
ShortId = elements[i].children[1].alt;//New style to take ShortID
if(DefaultCat==0)setDefaultCatToProductId(parseInt(ShortId));
arrSortedOrder[i]=parseInt(ShortId);
}
Any one know how to get to this value?
p.s
Plz Do NOT give me solution with $(.spanDefCat) because when i find deff=0 i need to take ShordId as Well from this element[i]
Try this:
$(".cart-item").each(function(){
var shortId = $(this).find(".bold-14").parent("li").siblings("li").children("span").html();
var shortItem = shortId.replace(' ','').split(":");
var defaultCat = $(this).find(".spanDefCat").html();
var item = defaultCat.replace(' ','').split(":");
if(item[1]==0){
var id = parseInt(shortItem[1]);
//do something
}else{
var id = parseInt(shortItem[1]);
//do something else
}
console.log(defaultCat);
console.log(shortId);
});
Note: Above code give you the DefaultCat:334 and ShortId:1010 so now you can use both in if else statement.
If the format of DefaultCat:334 is same for all cart item then you can check whether it is 0 or not
JSFIDDLE DEMO
I see JQuery tag so i give you a response with JQuery statements.
$(".cart-item").find(".spanDefCat").each(function(index, domEle){
//get text, delete spaces and split
split_result = $(domEle).text().replace(' ','').split(":");
//get only numeric value as string
defaultCat = split_result[1];
//parse into int
defaultCat = parseInt(defaultCat);
//if your var is equal to 0
if(defaultCat == 0){
/*********************
* Type you code here *
**********************/
}
});