Classes acculumating when cloning a template for a data array - javascript

I've ran into a problem when cloning a template div to create elements for a dataset.
The problem is that classes accumulate between creating the elements for each data record.
Example JS:
$(document).ready(function(){
var data = [
{cls: 'test1',text:'test1'},
{cls: 'test2',text:'test2'},
{cls: 'test3',text:'test3'}
];
for(var x in data)
{
var item = $('#itemTemplate').clone().removeClass('template');
item.addClass(data[x].cls).html(data[x].text);
$('#test-container').prepend(item);
}
});
And the HTML body:
<div id="test-container">
</div>
<div id="itemTemplate" class="template">
</div>
This produces:
<div id="test-container">
<div id="itemTemplate" class="test1 test2 test3">test3</div>
<div id="itemTemplate" class="test1 test2">test2</div>
<div id="itemTemplate" class="test1">test1</div>
</div>
Notice the test1 test2 test3 where it should just be test3. Am I missing something or just got it plain wrong?
Tested in jQuery 1.7 & 1.6.4.

you should also remove the id from the cloned elements. else it doesn't know which one he needs to clone
item.addClass(data[x].cls).html(data[x].text).removeAttr("id");

Changing the following line will remove all classes on the element, allowing you to add just the one you want.
var item = $('#itemTemplate').clone().removeClass()
The underlying problem is due to the fact that you're cloning the element whilst maintaining the ID. The next time you use the ID selector you're picking up multiple elements. Therefore t would also be worth changing the ID of the cloned element before appending it:
var item = $('#itemTemplate').clone().attr("id", data[x].text).removeClass()

Related

Using a variable value on multiple elements with the same iD

So I have a file like
<div class="u1">
<p id="level1"></p>
<p id="level2"></p>
</div>
<div class="u2">
<p id="level1"></p>
<p id="level3"></p>
</div>
and if I use something like
document.getElementbyId("level1").innerText(or innerHtml) = "Hello"
it writes that string on one element, and not on every element with id="level1"
I tried with
$('#level1').text("Hello");
but it works only for one.
I need something to write a string on every element with id="level1".
ID is a unique identifier, so you can't have few items with the same ID. Provided HTML code is invalid. You can use CLASS attribute or data-id or....
If you can't change HTML and it comes from third-party dependency, then we have a list of NOT recommended but WORKING solutions
jQuery:
$('[id="level1"]').text("Hello");
js:
var elements = document.querySelectorAll('#level1'); + for loop to iterate elements
And a lot of similar solutions based on knowledges how elements selecting works under the hood
It is ideal to have Unique Ids and thus you cannot update it this way. What you can do is you can give it a class like this:
<div class="u1">
<p id="level1" class="helloText"></p>
<p id="level2"></p>
</div>
<div class="u2">
<p id="level1" class="helloText"></p>
<p id="level3"></p>
</div>
Now use this JS to update the HTML:
var paras = document.getElementsByClassName("helloText");
for (i = 0; i < paras.length; i++) {
paras[i].innerHTML = "Hello";
}
You can also remove the Ids or make them unique to make your code cleaner. It is never recommended to use duplicate Ids.
You give a classname to the elements you need:
class = "levels"
in the script, use document.getElementsByClassName, which return an array with all elements. then you can loop through that array to get the info of each elements.
let levels = document.getElementsByClassName("levels");
for (let j=0; j<levels.length; j++){
levels[j] do something...;
}

How do I move these elements in DOM

I don't want to change HTML because I want to leave the display the way it is for default view and want to move them in second view. I want to know how I can dynamically order the class of a div.
I want to do this via button click. I have adEventListener() for 'click' where I am doing something and the move logic would go inside this event listener.
I understand that I can get these divs, remove from their parents and place it where I want. But I do not know how to do these for each of them since I have multiple lis. I am struggling with the loop so that I can do these for each li. I need to do this using pure JS and not jQuery.
<ul>
<li>
<div>
<div class="a">
<div class="b">
<a class="c">
<div class="d"></div>
<div class="e">
<div class="f"></div> // this is the first item that I want to move
</div>
<div class="g"></div> // this is the second item that I want to move
</a>
</div>
<div class= "h"></div> // I want above mentioned divs to be before this div
</div>
</div>
</li>
//There are multiples lis
<li></li>
Assuming you would like to do this on load of the page, you could solve your problem with the following JQuery DOM manipulations:
$(document).ready(function(){
$("ul .a").each(function(index, element){
$current_div_a = $(element);
$div_h = $current_div_a.find(".h");
$div_f = $current_div_a.find(".f");
$div_f.clone().insertBefore($div_h);
$div_f.remove();
$div_g = $current_div_a.find(".g");
$div_g.clone().insertBefore($div_h);
$div_g.remove();
})
});
You can test it out on this demo.
I strongly advise against this way of doing it though. I guess it's also the reason why your question got some downvotes too. Just modifying your HTML keeps your code clean, maintainable and clearer for anyone else starting to work on your project. Keeping backwards compatibility for your code as much as possible will cause maintainability problems later.
I ended up using
var list = document.querySelectorAll("ul li");
for (var item of list) {
let fClass = item.querySelector(".f");
fClass.parentNode.removeChild(fClass);
let parentOfFirstChildAfterWhichIwantMyF = item.querySelector(//selector for parentOfFirstChildAfterWhichIwantMyF);
parentOfFirstChildAfterWhichIwantMyF.insertAdjacentElement("beforeend", fClass);
}

Find instance following given element

I have a question about dom navigation with jquery. I'm trying to find an element with a given class that is closest in the dom following a given element.
I have a table like structure, created through divs and styled in css. I have an element being edited, and when the user presses enter I want to focus the following editable element. However, it's not a sibling of the element being edited.
HTML
<div class="calendarEntry">
<div when="2014,9,18" class="when">Sep 18</div>
<div class="items">
<div class="item">
<div code="ABC" class="type">ABC123</div>
<div offered="2014,9,15" class="offered dateish">Sep 15
<div class="offer editable">10</div>
<div class="sku editable">TH1</div>
<button>Publish</button>
</div>
</div>
<div class="item">
<div code="DEF" class="type">DEF321</div>
<div offered="2014,9,14" class="offered dateish">Sep 14
<div class="offer editable">10</div>
<div class="sku editable">TH2</div>
<button>Publish</button>
</div>
</div>
<div class="item">
<div code="GHI" class="type">GHI852</div>
<div offered="2014,9,12" class="offered dateish">Sep 12
<div class="offer editable">10</div>
<div class="sku editable">TH3</div>
<button>Publish</button>
</div>
</div>
</div>
</div>
Note: There are multiple calendar entries on the page.
Say the user is editing the offer of the DEF312 item. When they hit enter I want to edit the offer of GHI852. I have the code to make the div editable, by replacing it with a text field with a class of offer editing. If they're editing the final offer in this calendar entry, then the enter key should focus the first editable offer of the following calendar entry, if there is one. If we're at the bottom of the list I don't want to wrap back to the top (which I think would overly complicate matters anyway).
The bit I'm stuck with is how to find the next offer (all offers are editable).
Here's what I've tried:
var nextOffer = $('.offer').find('.editing').next('.editable');
Clearly, this doesn't work. The problem is that the following editable offer isn't a sibling of the current offer being edited, so next() doesn't work for me. The following offer could be in the current calendar entry, or it's just as likely to be in the next calendar entry. Either way, it's a few divs away, at varying depths.
Can I even do this with jquery dom traversals, or am I better just brute forcing it through javascript (i.e. looping through all .editable instances and returning the one after .editing?
Adding the class 'editing' to simulate the the input:
<div class="item">
<div code="DEF" class="type">DEF321</div>
<div offered="2014,9,14" class="offered dateish">Sep 14
<div class="offer editable">10</div>
<div class="sku editable editing">TH2</div>
<button>Publish</button>
</div>
</div>
you can do:
function findEditable(currentItem) {
var nextEditable = undefined,
selectors = [".item", ".calendarEntry"];
$.each(selectors , function (idx, selector) {
var ref = currentItem.closest(selector);
nextEditable = ref.parent()
.children("div:gt(" + ref.index() + ")")
.find(".offer.editable")
.first();
return nextEditable.length === 0;
})
return nextEditable;
}
findEditable($(".editing")).css({
color: 'red'
});
jsfiddle demo
You can use parents() to get the .offered element which contains the .offer element like so:
var offered = $('.offer').find('.editing').parents('.offered');
From that you can use next() to get into the .offered element's sibling .item element, and find the .editable element within that:
offered.next('.item').find('.editable');
JSFiddle demo. Note that I've manually added this .editing element within your DEF321 item's .offer element - I assume this gets added dynamically on your side, but either way isn't included in your question.
Edit: The HTML in the question has now been changed. Based on this, instead of getting the .offered parent, you'd get the .item parent:
var item = $('.offer').find('.editing').parents('.item');
And proceed in the same way as before:
item.next('.item').find('.editable');
JSFiddle demo.
try this
var current=document.activeElement,
all=$(".editable"),
index=all.indexOf(current),
next=all[index+1]
It first finds the current element and the list of elements,
then it will find the current element in the list.
It will then add 1 to the index and select it from the list.
To extend the array with the indexOf function;
if(!Array.prototype.indexOf){
Array.prototype.indexOf=function(e/*,from*/){
var len=this.length>>>0,
from=Number(arguments[1])||0;
from=(from<0)?Math.ceil(from):Math.floor(from);
if(from<0)from+=l;
for(;from<len;from++){
if(from in this&&this[from]===e)return from;
}
return -1;
};
}

Jquery remove element from var

I have a page with a parent div and several child divs. These are generated dynamically. I need to save the structure (html code) of all the child divs in my database, so I get the html content using .html().
But before I save the content in db, I need to remove one or more child divs. (But these divs will still need to be on the browser page)
How do I use the remove method with the selector (in this example I need to remove child3) on the output of .html()
<div id="graph-parent">
<div id="child1"></div>
<div id="child2"></div>
<div id="child3"></div>
<div id="child4"></div>
<div id="child5"></div>
</div>
var htmlContent = $( "#graph-parent" ).html();
how do I remove child3 from htmlContent?
Simply clone the element and operate on that:
var clone = $('#graph-parent').clone();
clone.find('#child3').remove();
var htmlContent = clone.html();
I think you need something like this:
http://jsfiddle.net/glegan/65QPV/
var clone = $('#graph-parent').clone();
clone.find('#child1').remove();
clone.find('#child5').remove();
var htmlContent = clone.html();
alert(htmlContent);

jQuery Masonry remove function example

I have implemented jQuery masonry to our site and it works great. Our site is dynamic and users must be able to add/remove masonry box's. The site has an add example but no remove example. Our db is queried returning x number of items. Looping through they are loaded and displayed. Here's a code sample: (we are use F3 framework and the F3:repeat is it's looping mechanism.).
<div id="container" class="transitions-enabled clearfix" style="clear:both;">
<F3:repeat group="{{#productItems}}" value="{{#item}}">
<div id="{{#item.itemId}}">
<div class="box">
<div class="view"> <!-- for css -->
<a onclick='quickRemove("{{#item.itemId}}")>
<img src="{{#item.pic}}" />
</a>
</div>
<p>
{{#item.title}}
</p>
</div>
</div>
</F3:repeat>
</div>
In the javascript code the item id number is unique and is passed into the function. It's also the div id# to distinguish each box. I've tried various combinations and methods but can't seem to get this to work.
function quickRemove(item){
var obj = $('#'+item+'').html(); // item is the product id# but also the div id#
$('#container').masonry('remove',obj);
$('#container').masonry('reloadItems');
$('#container').masonry('reload');
}
Has anyone out there successfully removed an item and how did you do it?
Thx.
Currently you appear to be passing a string full of html to the masonry remove method. Pass it the actual jQuery wrapped element by not including .html()
function quickRemove(item){
var obj = $('#'+item+''); // item is the product id# but also the div id#
$('#container').masonry('remove',obj);
$('#container').masonry('reloadItems');
$('#container').masonry('reload');
}

Categories

Resources