JQuery: elegant way to replace set of elements with another set? - javascript

I have a bunch of DOM like
<div>
<div class="stuff"/>
<div class="stuff"/>
<div class="stuff"/>
</div>
and I want to replace it with a new set of stuff
<div>
<div class="stuff"/>
<p class="stuff"/>
<ul class="stuff"/>
<a class="stuff"/>
</div>
Which will be fetched via Ajax. My question is: what is the best way to do this?
$.replaceWith doesn't quite do what I want, because I then end up with multiple copies of the new stuff.
I can guarantee that all the stuff will be in one contiguous block, and so presumably I could put in some placeholder after the last element (or before the first element) of the old stuff, remove the old stuff, and replace the placeholder with the new stuff.
However, this seems rather roundabout and inelegant. Is there any clever way of, removing all the old stuff and putting in a single copy of the new stuff, all at one go?
EDIT: I would also like to do this without using any container divs. Using container divs would work in the above case, but would fail in some cases, like when the stuff is inside a <table>:
<table>
<head/>
<body>
<tr/>
<tr class="stuff"/>
<tr class="stuff"/>
<tr class="stuff"/>
<tr/>
</body>
</table>
If i want to replace the rows labelled stuff with another set of rows, possibly more, possibly fewer, there is no way I can nicely put them in a container thingy without breaking the HTML, since the <body> can only contain <tr>s (IIRC).

$('#outerdiv').empty().append(newContent);
Unlike .html(), this will work regardless of whether newContent is an HTML string, or an existing DOM structure.
If there are multiple elements to be replaced but where you need to retain their siblings, you can do this:
$('.stuff').first().before(newContent).end().remove();
i.e. take the first .stuff element, add the new content before it, and then remove all the .stuff elements.

Yes: $('#tagetDiv').html(newContent)

One way to do it would be with wrapAll:
$('.stuff').wrapAll('<div/>').parent().replaceWith('<div class="stuff"/>');
I'm not sure if that passes the "elegant" test, but it does work regardless of whether there is any other content in the containing element.
With that said, though, this seems to be a very complicated solution to a simple problem. The simple solution would be to wrap your elements in a containing element; this shouldn't be a problem if, as you say, you can guarantee that they will always be together.

Related

Is it possible to prevent the browser from parsing certain elements?

Consider the following markup:
<div hidden id="table-template">
<table>
<tbody>
<slot></slot>
</tbody>
</table>
</div>
<div hidden id="table-row-template">
<tr>
<td>
Some Content
</td>
</tr>
</div>
I would like to use 'table-template' and 'table-row-template' as re-usable components in my script.
(clone them and append them on demand)
but as the page loads the browser parses the markup and mutates it (taking 'slot' element and insert it before the 'table' element, and stripping 'td' and 'tr' tags).
This is reasonable (not valid HTML of course), but is there any way I can prevent the browser from parsing those elements?
So far I have tried:
using hidden elements,
wrapping with 'pre'/'code' tags,
but none seem to work.
You can try using <script type="text/template"></script>
<script id="mytemplate" type="text/template">
...your table's html...
</script>
Then:
<script>
alert($('#mytemplate').html());
</script>
Many libraries use this method, handlebar.js for example.
The <template> element may gain compatibility with more browser versions: http://caniuse.com/#feat=template
Use a fictional tag that the browser wouldn't recognise. You can still target it with Javascript and read it's text content but the browser won't parse it.
Say <template id="the-template">Foo bar</template>.
But really the best way to re-use html snippets it to create them in Javascript without polluting the DOM.
e.g
var elToReuse = document.createElement('div').innerHTML('<h1>Lets have title here</h1>');
// Let's do some things to the elToReuse
var anotherVersion = elToReuse.querySelector('h1').innerText('My another title');
// Now it's the time to append to the DOM
document.body.appendChild(anotherVersion);
So I have researched a bit about my problem, and this is what i figured out -
the 'tbody' element accpets only 'tr' elements as children.
so browsers will 'fix' the markup while parsing it one way or the other.
using template tags as vassiliskrikonis suggested will not work for the 'table-template' but will fix the 'table-row-template' issue,
using script tag as #Mihaly_KR suggested will work for both, but when accessing the template you will get a plain string to work with (injecting this string into the DOM will make the browser parse it and 'fix' it once again...)
The workaround I have found is to use other tag as a placeholder for my content.
one way is using a comment e.g.
but accessing this comment element requires a use of treeWalker or nodeIterator (which are much less performant then querySelector or getElementsByTagName)
other way is using a script tag as a placeholder.
browsers will not change the markup, and it can be accessed later using querySelector or getElementsByTagName.
so my refactored code looks like this:
<template id="table-template">
<table>
<tbody>
<script type="slot"></script>
</tbody>
</table>
</template>
<template id="table-row-template">
<tr>
<td>
Some Content
</td>
</tr>
</template>

JQuery accessing text deep below unique id

I'm trying to make an app that pulls our data from our existing website. As we have no known API, I have decided to use ajax with whateverorigin.org to pull the full HTML from our site, and parse it with jQuery from there.
Unfortunately, much of the data I need lies far below any unique ID's. With $(data.contents).find("#unique").text() I am able to retrieve data of the type:
<div id="unique">
<div>
Text I want
</div>
</div>
However, as the nesting gets deeper, like:
<span id="unique2">
<table>
<tbody>
<tr>
<td>
Text I want
that snippet always returns "".
I have tried chaining the levels together with .join, but that doesn't work either.
Code in context here, around line 40-50. The site I'm trying to access is wmbr.org. The id that works is #now_on_the_air, one of the id's that doesn't is #recent_plays. The log is empty.

Jquery find and append performance issue when building big hierarchy

I need to recursively read a hierarchic JSON object and build the HTML the same way as children objects will have to be inserted inside parents object.
Example:
<div class="level0">
<div class="children1 level1">
<div class="children1 level2">
.... N children
</div>
... N Children
</div>
</div>
... N Parents
Now, I'm building this with the following code:
function buildDivHtml(); // This generates all the parent/children code at that particular moment, meaning one by one.
object.find('selector of the row where the append goes').append('html from above function');
This causes browser to freeze. Is there a way to do this that works flawlessly without browser freezing?

Using CSS or Java to Show or Hide Empty Content Area

I am trying to create a page template that uses section headers and subsequent content that is being dynamically pulled in based on a separate database. Currently I have the page set up to look something like this:
<tr>
<td>
<h3>Product Applications</h3>
{tag_applications}<br />
</td>
</tr>
Where the Product Applications is a formatted header on the page and the {tag_applications} is link through the CMS that is pulling in content from a field defined elsewhere. I am trying to figure out how to hide the entire cell (or div if I need to) with either CSS or a script when the {tag_applications} is empty or blank. I tried to use the 'empty' tag in CSS on the cell and setting the display to hidden, but of course, the cell is not actually empty because of the header.
What is the best way that I can accomplish this without creating separate pages for each item?
Thanks!
Try wrapping your content area in a div, like this:
<tr>
<td>
<h3>Product Applications</h3>
<div class="contentSection">{tag_applications}</div>
</td>
</tr>
Then your script can check if the div is empty or not. (uses jQuery)
$(function()
{
$(".contentSection").each(function(idx, ele)
{
if($(ele).html() == "")
$(ele).parent().hide();
});
});
My apologies if I made any syntax errors...

AND/OR Conditions in jQuery

In jQuery space denotes AND condition "," denotes OR condition, Is that right? But I am facing issues in that. Here is my sample html code
<td id="4">
<div id="test1" class="test1"></div>
<div id="test2" class="test2"></div>
</td>
<td id="5">
<div id="test1" class="test1"></div>
<div id="test2" class="test2"></div>
</td>
If I use the following query, it works
jQuery('#4 [id*=test1]')
it selects the correct div. However, if I use this query,
jQuery('#4 #test1')
it doesn't work. Any Idea?
It is not valid to have duplicate ids within the same document.
If you are building this dynamically then try prepending the parent id to the child so it would be like:
<td id="r4">
<div id="r4_test1" class="test1"></div>
<div id="r4_test2" class="test2"></div>
</td>
<td id="r5">
<div id="r5_test1" class="test1"></div>
<div id="r5_test2" class="test2"></div>
</td>
Note, starting an ID with a number is also invalid, so I took the liberty to prepend "r" to your row ids.
I would recommend using the selector:
$('#r5 .test1')
Space isn't strictly an 'and' condition.
In your own example, jQuery('#4 #test1') space means to get children of #4 called #test1 if you see what I mean
The jquery docs for this explain it better than I do!
jQuery('ancestor descendant')
Selects all elements that are descendants of a given ancestor.
Thinking of selectors in terms of "AND" and "OR" probably isn't the most helpful way to go about things. If a space actually meant "AND", then these two statements would be identical:
$('.parent_class .child_class')
$('.child_class .parent_class')
If a selector was a simple "AND", then these statements would select all items that meet both criteria.
In reality, these statements are very different. A space in jQuery and CSS selectors actually shows inheritance. When you have two separate classes, as in my example, you're always saying "select the class that is second in the list, only if it is contained by an element with the first class in the list."
You could say that a comma means "OR", but really it just separates two selecting statements from each other, so that you can select two completely separate items or groups of items.
The jQuery selector syntax borrows from CSS, so this group of tutorials on w3schools.com might be a helpful place to start.
I think you ought to drop the ids off the div tags and just work with classes.
use $('#d5 .test1') or
$('#d5').find('.test1')
I usually use the latter because it's just easier to read when I go back a month later to look at the code.
<td id="d4">
<div class="test1">w</div>
<div class="test2">x</div>
</td>
<td id="d5">
<div class="test1">y</div>
<div class="test2">z</div>
</td>

Categories