I have a large HTML source, and I'd like to break it into multiple parts. I've been able to achieve most of this task, but I'm struggling with a single aspect.
When all of the HTML is wrapped in tags, I have no problem; however, if text nodes are mixed with HTML tags, I'm unable to capture all parts.
What am I doing wrong?
Below, is a jsFiddle that shows an example of the problem.
http://jsfiddle.net/acrashik/aDm8L/1/
Here is the code I have written so far to attempt to break up the HTML:
function parseElement(selector,parts, cycle) {
var cc = $(selector),
content = cc.children(),
total = content.length,
maxHeight = cc.height(),
spaceLeft = maxHeight,
cycle = cycle || 0;
function addToPage(elem,elemSize) {
elem.appendTo(parts[cycle]);
spaceLeft -= elemSize;
}
function startNewPage() {
cycle++
parseElement(selector,parts, cycle);
}
$.each(content,function(index,v){
var elem = $(v),
tag = elem[0].tagName.toLowerCase(),
elemSize = elem.outerHeight(true);
if (elemSize <= spaceLeft) {
addToPage(elem,elemSize);
} else if (elemSize > spaceLeft) {
startNewPage();
}
});
}
Question
How can I parse all the HTML text, even unwrapped text nodes, preserving structure and order?
Update
Thanks for help, case solved, only possible way to measure text node height is to wrap it, here is code how to achieve that:
$(selector).contents().filter(function(){
return this.nodeType === 3
}).wrap('<span />');
and here is fully working example:
http://jsfiddle.net/acrashik/aDm8L/20/
thanks everyone.
The problem you are facing comes from using jQuery.children():
Note also that like most jQuery methods, .children() does not return
text nodes;
Instead, you should use the native DOM property, childNodes which does contain text nodes:
cc[0].childNodes;
Or, better yet, pass in the reference to the DOM node:
function parseElement(element, parts, cycle) {
var cc = $(element),
content = element.childNodes,
total = content.length,
maxHeight = cc.height(),
spaceLeft = maxHeight,
cycle = cycle || 0;
//Your other functions down here...
}
And call like:
parseElement(document.getElementById('source2'), ['#part3','#part4']);
Notice You'll need to revisit your other methods to accommodate these changes.
You can use css to split into several columns. I don't know your targeted browsers but here is the code (only on modern browsers) :
html :
<div class="columns">
very long text split into 4 columns
</div>
css :
.columns {
-webkit-columns: 4;
-moz-columns: 4;
columns: 4;
}
sample here : http://codepen.io/raphaelgoetter/pen/ehfxb
Related
I'm trying to get this jQuery parallax code to work but I don't want to spaghetti everything. How can it be looped to apply to multiple element IDs?
(it doesn't work with classes because the function needs to run multiple times specific to each particular div) - I'm not very good when it comes to looping, still learning how to do this stuff.
Anyway, this is a functioning code for one section (a div with a child div, #about > #pAbout in this instance):
$(document).ready(function() {
if ($("#pAbout").length) {
parallax();
}
});
$(window).scroll(function(e) {
if ($("#pAbout").length) {
parallax();
}
});
function parallax(){
if( $("#pAbout").length > 0 ) {
var plxBackground = $("#pAbout");
var plxWindow = $("#about");
var plxWindowTopToPageTop = $(plxWindow).offset().top;
var windowTopToPageTop = $(window).scrollTop();
var plxWindowTopToWindowTop = plxWindowTopToPageTop - windowTopToPageTop;
var plxBackgroundTopToPageTop = $(plxBackground).offset().top;
var windowInnerHeight = window.innerHeight;
var plxBackgroundTopToWindowTop = plxBackgroundTopToPageTop - windowTopToPageTop;
var plxBackgroundTopToWindowBottom = windowInnerHeight - plxBackgroundTopToWindowTop;
var plxSpeed = 0.35;
plxBackground.css('top', - (plxWindowTopToWindowTop * plxSpeed) + 'px');
}
}
I was hoping to create an array like this:
var ids = ['#pAbout', '#pConcept', '#pBroadcast', '#pDigital', '#pDesign', '#pContact'];
But I can't get the e business to work unfortunately, it's very frustrating for me. Any help would be greatly appreciated!
You can use multiple selector in jQuery to select disparate elements by simply using a comma between the selectors.
$("#pAbout, #pConcept, #pBroadcast, #pDigital, #pDesign, #pContact")
.each(function(){
//manipulate element here
});
That each() iterates over all matched elements so no need to check for length etc.
let me just give a quick story. I have made a page. (VERY simple - two divs with a different background image, see here.)
Anyway, I need to make it so that when a new page loads, the two divs that I have load in a random order over and over, filling the entire screen content. So there's no pattern of the first div and then the second, it's just randomly generated. Sort of like a huge grid, with the two divs repeated with no pattern.
My question is...is that possible? I assume I'd need to know PHP, but I have no knowledge of it.
Thanks guys, I appreciate all help!
http://jsfiddle.net/uYPRq/
jquery
var div1 = '<div class="one">';
var div2 = '<div class="two">';
var len =
Math.floor(window.innerWidth/30)*Math.floor(window.innerHeight/30);
for (x = 0; x < len; x++) {
if ( Math.random() > 0.5 ) {
$(div1).appendTo('body');
}
else {
$(div2).appendTo('body');
}
}
css
div.one, div.two {
height:30px;
width:30px;
float:left;
}
div.one { background-color:#EBE1E4; }
div.two { background-color:#F0F5DF; }
edit:
changed screen.availWidth to window.innerWidth
Something like so? Just loop through how ever many times you like and add elements in.
for (i = 0; i < 300; i++) {
var type1 = document.createElement("div");
var type2 = document.createElement("div");
type1.innerHTML = "div1";
type2.innerHTML = "div2";
type1.setAttribute("class", "type1");
type2.setAttribute("class", "type2");
document.body.appendChild(type1);
document.body.appendChild(type2);
}
No PHP needed. This can be done client-side using Javascript (Jquery might be easier).
I'm looking for a way to change some table headers. And some of these headers contain addition HTML code, like line breaks.
This is the typical setup of the table on the page (page source):
<table class="awesometable" style="width: 100%; margin-right: 0;">
<tr>
<th style="background: red; color: white;">Male<br /> contestant</th>
<th style="background: red; color: white;">Female<br /> contestant</th>
</tr>
</table>
Where Male<br /> contestant, Female<br /> contestant (and a series of other slight variation) should be changed to a simple symbol like ♂ and ♀.
Using document.body.innerHTML.replace() replaces the entire DOM with new nodes, which is undesired, and can also change other instances were the text in the table header is also in plain text and were it shouldn't be changed, although this is rare, and only a minor issue.
Using a selective nodeType == 3 fails on the additional line breaks.
Any ideas are welcome, code examples (here or at, for example, jsfiddle) are always preferred. Thanks.
Try this:
var headers = document.getElementsByTagName('th');
headers[0].innerHTML = '♂';
headers[1].innerHTML = '♀';
Shows the concept. Better to give the respective cells an id or class to identify them rather than using the content. You could also do something like:
var cell, headers = document.getElementsByTagName('th');
for (var i=0, iLen=headers.length; i<iLen; i++) {
cell = headers[i];
if (cell.innerHTML.match('Male') ) {
cell.innerHTML = '♂';
}
else if (cell.innerHTML.match('Female')) {
cell.innerHTML = '♀';
}
}
I'd recommend using jQuery just because it's so much friendlier when dealing with the DOM. Note also that it's probably slightly faster -- which may matter if you have a large table -- not to assign something to innerHTML until you've done all the operations on the string. That is, store the value of innerHTML in a variable, run replace functions, then place the result back in innerHTML. (Note also that I'm replacing your <br /> with <br>. At least in Chrome, innerHTML will convert XHTML closed single tags to valid HTML, which here is <br>.)
Here's a more extensible method (jsFiddle Example):
var replacements = [
["Male<br>contestant", "♂"],
["Female<br>contestant", "♀"]
];
function runReplacement() {
$(".awesometable th").each(function() {
var $this = $(this);
var ih = $this.html();
$.each(replacements, function(i, arr) {
ih = ih.replace(arr[0], arr[1]);
});
$this.html(ih);
});
}
In addition to the above, you can use .text() instead of .html() by just ignoring the <br> part of each cell (e.g. replacing "Malecontestant" instead of "Male<br>contestant"). Example here.
EDIT:
And for correcting an example below:
var headers = document.getElementsByTagName("th");
for (var i = 0; i < headers.length; i++) {
headers[i].innerHTML = headers[i].innerHTML.replace("Male<br>contestant", "♂")
.replace("Female<br>contestant", "♀");
}
For silliness like focusing on terseness (70 characters shorter, ignoring excess whitespace):
$("th").each(function(){$(this).html($(this).html()
.replace("Male<br>contestant","♂").replace("Female<br>contestant","♀"));});
What's the problem with something like
function replace(text)
{
return text.replace('Male', '♂').replace('Female', '♀');
}
var ths = document.getElementById('myTable').getElementsByTagName('th'),
i = ths.length,
th, text;
while (i-- && (th = ths[i], text = th.innerText))
{
if (text)
{
// IE, Safari, Chrome, Opera
th.innerText = replace(text);
}
else
{
// Everything but IE <9
th.textContent = replace(th.textContent);
}
}
As requested, jsFiddle: http://jsfiddle.net/mattball/4EwGt/
Yes, innerText || textContent is a PITA.
I want to replace the entire header, Male<br /> contestant and Female<br /> contestant, not just the first word, and trying the whole in your example doesn't work. Only seems to remove the line break, doesn't change the text.
Use this replace function instead:
function replace(text)
{
return text.replace('Male\ncontestant', '♂')
.replace('Female\ncontestant', '♀');
}
Demo: http://jsfiddle.net/mattball/8W3cM/
I'm a bit of a novice when it comes to Javascript, but I've managed to create this script which 'greys out' text and inputs found in a div. It accepts a boolean (show) to declare whether the elements are being hidden or reshown, as well as the name of the div(s) to hide.
It works exactly as intended in Chrome and Firefox, but IE won't do a thing. Through 'debugging' using alerts, I think the issue lies with this line:
var div = document.getElementsByName(divName);
...of the following code:
function hideAndShow(show, divName) {
var hideColor = "#DFDFDF";
// Find all matching divs and loop through
var div = document.getElementsByName(divName);
for (var count1 = 0; count1 < div.length; count1++) {
// Find and loop through all elements in div
var elements = div[count1].getElementsByTagName("*");
for (var count2 = 0; count2 < elements.length; count2++) {
if (elements[count2].tagName == "TEXTAREA" || elements[count2].tagName == "INPUT") {
elements[count2].disabled = !show; //Disable
elements[count2].style.borderColor = (show) ? "" : hideColor; // Change border colour
elements[count2].value = ""; //Clear existing text
}
}
// Change the colour of anything left, such as text
div[count1].style.color = (show) ? "" : hideColor;
alert(div[count1].id);
}
}
Can anybody please help or point me in the right direction? I'm stumped!
It's possible that IE is getting confused by your page: http://www.romantika.name/v2/javascripts-getelementsbyname-ie-vs-firefox/
afaik the IE implementation of getElementsByName actually searches on id
In IE7 at least:
// works in IE but not Chrome
<div id="test"></div>
alert(document.getElementsByName('test').length);
// doesn't work in IE, works in Chrome
<div name="test"></div>
alert(document.getElementsByName('test').length);
Libraries like jQuery deal with all this nonsense for you and make selecting DOM elements trivial.
If you want to do it in pure JS, you might want to look at providing an implementation of getElementsByClassName (see here for an example) to solve the problem.
I'm relatively new to Javascript and was wondering if there's a quick way to shuffle content that is contained in multiple <div> tags. For example
<div id='d1'>
<span>alpha</span>
<img src='alpha.jpg'>
</div>
<div id='d2'>
<span>beta</span>
<img src='beta.jpg'>
</div>
<div id='d3'>
<span>gamma</span>
<img src='gamma.jpg'>
</div>
<button onclick='shuffle_content();'>Shuffle</button>
After clicking on the button, I'd like the content in d1, d2, d3 to change places (for example maybe d3 would be first, then d1, then d2).
A quick way to kind of move things around is to copy the first div element (d1), then put it at the very end (after d3), and then delete the original d1. But that doesn't really randomize things. It just makes things go in the cycle (which might be ok).
Any suggestions would be appreciated. Thanks.
are you ok with using a javascript library like jQuery? here's a quick jQuery example to accomplish what you're after. the only modification to your HTML is the addition of a container element as suggested:
<div id="shuffle">
<div id='d1'>...</div>
<div id='d2'>...</div>
<div id='d3'>...</div>
</div>
and javascript:
function shuffle(e) { // pass the divs to the function
var replace = $('<div>');
var size = e.size();
while (size >= 1) {
var rand = Math.floor(Math.random() * size);
var temp = e.get(rand); // grab a random div from our set
replace.append(temp); // add the selected div to our new set
e = e.not(temp); // remove our selected div from the main set
size--;
}
$('#shuffle').html(replace.html() ); // update our container div with the
// new, randomized divs
}
shuffle( $('#shuffle div') );
A recent question was just closed as duplicate of this, but I feel I've got a better answer than any here. This method is very direct. There's no mucking with copying HTML, thus preserving changes to the DOM, styles, event handlers, etc.
To shuffle all the children of some parent element, select a random child and append it back to the parent one at a time until all the children have been re-appended.
Using jQuery:
var parent = $("#shuffle");
var divs = parent.children();
while (divs.length) {
parent.append(divs.splice(Math.floor(Math.random() * divs.length), 1)[0]);
}
Demo: http://jsfiddle.net/C6LPY/2
Without jQuery it's similar and just as simple:
var parent = document.getElementById("shuffle");
var divs = parent.children;
var frag = document.createDocumentFragment();
while (divs.length) {
frag.appendChild(divs[Math.floor(Math.random() * divs.length)]);
}
parent.appendChild(frag);
Demo: http://jsfiddle.net/C6LPY/5/
Edit: Here's a break down of the code:
// Create a document fragment to hold the shuffled elements
var frag = document.createDocumentFragment();
// Loop until every element is moved out of the parent and into the document fragment
while (divs.length) {
// select one random child element and move it into the document fragment
frag.appendChild(divs[Math.floor(Math.random() * divs.length)]);
}
// appending the document fragment appends all the elements, in the shuffled order
parent.appendChild(frag);
You can grab the content of each div
c1 = document.getElementById('div1').innerHTML
c2 = document.getElementById('div2').innerHTML
c3 = document.getElementById('div3').innerHTML
Then determine a new order for them randomly .. and then put each content in the new destination
say for instance, the randomness gave:
c1_div = 'div2'
c2_div = 'div1'
c3_div = 'div3'
then you just:
document.getElementById(c1_div).innerHTML = c1
document.getElementById(c2_div).innerHTML = c2
document.getElementById(c3_div).innerHTML = c3
Expanding on the nice answer by #gilly3, using jQuery one can actually avoid appending randomly-chosen elements of divs in a loop, by randomly sorting divinstead and appending them all at once:
$(function() {
var parent = $("#shuffle");
var divs = parent.children();
divs.sort(function(a, b) {
return 0.5 - Math.random();
});
parent.append(divs);
});
Demo: http://jsfiddle.net/ey70Lxhk/
Note however that this technique is not accurate in terms of randomness, and relies on sort which does not scale linearly with the number of elements.
I'd use server side code to accomplish this. I know this isn't really an answer to your question, but it is an alternative implementation.
Best Regards, Frank
I'd wrap the divs in an outer div, then pass its id to shuffle_content().
In there, you could create a new div, cloning the wrapper div's nodes in a random order to fill it, then replace the wrapper div with the new div.
For your HTML, the short answer to your question is:
function shuffle_content() {
var divA = new Array(3);
for(var i=0; i < 3; i++) {
divA[i] = document.getElementById('d'+(i+1));
document.body.removeChild(divA[i]);
}
while (divA.length > 0)
document.body.appendChild(divA.splice(Math.floor(Math.random() * divA.length),1)[0]);
}
To get there I wrote the following, which I think works better:
<html>
<div id="cards">
<div id="card0">Card0</div><div id="card1">Card1</div>
<div id="card2">Card2</div><div id="card3">Card3</div>
<div id="card4">Card4</div><div id="card5">Card5</div>
<div id="card6">Card6</div><div id="card7">Card7</div>
<div id="card8">Card8</div><div id="card9">Card9</div>
</div>
<button id="shuffle">Shuffle</button>
<script language="javascript">
<!--
document.getElementById('shuffle').onclick = function () {
var divCards = document.getElementById('cards');
var divCardsArray = new Array(
document.getElementById('card0'),
document.getElementById('card1'),
document.getElementById('card2'),
document.getElementById('card3'),
document.getElementById('card4'),
document.getElementById('card5'),
document.getElementById('card6'),
document.getElementById('card7'),
document.getElementById('card8'),
document.getElementById('card9')
);
return function() {
var mDivCardsArray=divCardsArray.slice();
while (divCards.childNodes.length > 0) {
divCards.removeChild(divCards.firstChild);
}
while (mDivCardsArray.length > 0) {
var i = Math.floor(Math.random() * mDivCardsArray.length);
divCards.appendChild(mDivCardsArray[i]);
mDivCardsArray.splice(i,1);
}
return false;
}
}()
//-->
</script>
</html>
I was trying to pack down that last while statement to:
while (mDivCardsArray.length > 0) {
divCards.appendChild(
mDivCardsArray.splice(
Math.floor(Math.random() * mDivCardsArray.length)
,1)[0]
);
}
but this is pretty hard to read and prone to error.
Going with jQuery or Prototype you could follow the same basic structure and get the result you're looking for.
Personally, I think it looks even better if you add 2 more divs to the cards stack, expand the divCardsArray, insert the following style block, and add this code right after the divCardsArray definition.
<html>
...
<style>
html,body{height:100%;width:100%;text-align:center;font-family:sans-serif;}
#cards,#cards div{padding:5px;margin:5px auto 5px auto;width:100px;}
</style>
...
<div id="cardA">CardA</div><div id="cardB">CardB</div>
...
var colorCardsArray = new Array(
'#f00', '#f80', '#ff0', '#8f0', '#0f0', '#0f8',
'#0ff', '#08f', '#00f', '#80f', '#f0f', '#f08' );
for(var i=0;i<divCardsArray.length;i++)
divCardsArray[i].style.backgroundColor=colorCardsArray[i];
...
</html>
I would suggest you randomize the content, not the actual Divs themselves. You could accomplish this by putting the content in separate html pages - no header info or body, just the content.
Then use a function on page load to randomly assign which div gets what content and use this to change the DIV's content:
<script type="text/javascript">
function ajaxManager(){
var args = ajaxManager.arguments;
if (document.getElementById) {
var x = (window.ActiveXObject) ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
}
if (x){
switch (args[0]){
case "load_page":
if (x)
{
x.onreadystatechange = function()
{
if (x.readyState == 4 && x.status == 200){
el = document.getElementById(args[2]);
el.innerHTML = x.responseText;
}
}
x.open("GET", args[1], true);
x.send(null);
}
break;
case "random_content":
ajaxManager('load_page', args[1], args[2]); /* args[1] is the content page, args[2] is the id of the div you want to populate with it. */
break;
} //END SWITCH
} //END if(x)
} //END AjaxManager
</script>