Counting element height in Javascript - javascript

I have such a Javascript code:
$content.each(function(i) {
var $el = $(this);
// save each content's height (where the menu items are)
// and hide them by setting the height to 0px
$el.data('height', $el.outerHeight(true)).css('height', '0px').show();
});
In such a situation:
<div id="sbi_container" class="sbi_container">
<div class="sbi_panel" data-bg="images/1.jpg">
About
<div class="sbi_content">
<ul>
<li>Subitem</li>
<li>Subitem</li>
<li>Subitem</li>
</ul>
</div>
</div>
<div class="sbi_panel" data-bg="images/2.jpg">
...
</div>
I don't know why, but when only an <ul> (which height we are counting) have a <li> with more than a one word in it, it adds a separate space below <ul>.
When there is only one long word, everything is ok.
Any ideas? :)

Try this
.your_ul { font-size: 0; }
.your_ul li { font-size: 1234px }
it might work.

Related

Separating content within div with Jquery

I have the following HTML:
<div class="page">
<div class="somecontent_1">...</div>
<div class="somecontent_2">...</div>
<div class="somecontent_3">...</div>
<div class="somecontent_4">...</div>
</div>
Now I'd like to separate the content with a separate page so it looks something like this:
<div class="page">
<div class="somecontent">...</div>
<div class="somecontent">...</div>
</div>
<div class="newpage">
<div class="somecontent">...</div>
<div class="somecontent">...</div>
</div>
The function checks the height of each class somecontent and if it's larger than a certain amount, I need to move the content to a new page.
My guess is that I would need to create an empty div (newpage) and then fetch the elements after the height is exceeded and move them to the empty newpage and continue iterate like that.
My question would be how I would get all content that are after the last element that reached the height so I can move it to the new empty page that I would create. Other solutions are most welcome if there is a better way of doing it!
The code I came up with looks like this:
var page = $('.page');
var pageHeight = 0;
$.each(page.find('.somecontent'), function() {
if (pageHeight > 1000) {
page.next('<div class="newpage"></div>');
/* Somehow get all elements to add to the newly created page */
page.next('.newpage').append(<NEXT_ELEMENTS>);
pageHeight = 0;
}
pageHeight = pageHeight + $(this).height();
});
When you reach the page which answers the height criterion use the .nextAll function to get all the next siblings of it, then use .wrapAll to wrap them with your newpage div.
Here is the corresponding documentation of nextAll and wrapAll, it has everything you need to cover your scenario.
See comments in line below.
// Instead of the $.each() utiility function
// Just loop over each content area that needs
// examination
$('.somecontent').each(function(index, item) {
// Check if the height of the item is greater than the target (20px for this example)
if (parseInt(getComputedStyle(item).height,10) > 20) {
// Make a new div after the div that the item is currently in
// if one doesn't already exist
if($(".newpage").length === 0){
$(item.closest(".page")).after('<div class="newpage"></div>');
}
// Move the item into the new div
$(item.closest(".page")).next('.newpage').append(item);
}
});
//console.log(document.body.innerHTML); // shows resulting HTML
div.page {border:1px solid red; }
div.newpage {border:1px solid green; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="page">
<div class="somecontent">This<br>is<br>some<br>content</div>
<div class="somecontent">This is some content</div>
<div class="somecontent">
<ul>
<li>This</li>
<li>is</li>
<li>some</li>
<li>content</li>
</ul>
</div>
<div class="somecontent">Other</div>
</div>

removeClass() is completely deleting elements from DOM

This is a build off of my previous question: Select a child of a child
I now have a <ul> within another <ul>. The behavior is an expandable menu. I'm doing this by adding and removing classes. For some reason...on the sub list - it completely removes the <li> elements from the DOM rather than just toggling it's classes. Why would it do that!?
You can see an example below:
$(function() {
// main expansion element
$(".expander").click(function() {
var subShown = $("ul > li", this).hasClass("show");
if (!subShown) {
$(".indented", this).slideDown('100').addClass("show");
$(".caret", this).addClass("reversedCaret");
} else {
$(".indented", this).slideUp('100').removeClass("show");
$(".caret", this).removeClass("reversedCaret");
}
});
// sub expansion element
$(".sub-expander, .caret").click(function() {
var subSelectText = $(".sub-expander").text();
if (subSelectText != "More") {
$(".indented--sub", this).slideUp('100').removeClass("show");
$(".caret", this).removeClass("reversedCaret");
$(".more-or-less").text("More");
} else {
$(".indented--sub", this).slideDown('100').addClass("show");
$(".caret", this).removeClass("reversedCaret");
$(".more-or-less").text("Show Less");
}
});
// stop propagation on the link element within .expander class
$(".indented").click(function(event) {
event.stopPropagation();
});
});
.expander:hover {
cursor: pointer;
}
.sub-expander--indented {
padding: 0 0 0 23px;
}
.sub-caret {
margin-right: 75px;
}
.indented,
.indented--sub {
display: none;
}
.show {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<div class="expander">
<span class="caret downCaret right visibleCaret">+</span>
<ul>
<li class="category">Item 1
</li>
<li class="indented">Item 2</li>
<li class="indented">Item 3
<ul class="sub-expander indented more" style="padding-top:
0px;">
<li class="indented--sub">Chapter 5</li>
<li class="indented--sub">Chapter 6</li>
<li class="indented--sub">Chapter 7</li>
<span class="sub-caret moreCaret visibleLessCaret right">+</span>
<li class="more-or-less less sub-expander--
indented">More</li>
</ul>
</li>
</ul>
</div>
I'm giving it a separate classname to differentiate from the main section so that they don't show on initial open, so I'm not sure why it is behaving the way it is. It seems like there is a better way to go about this but I don't know what that would be.
UPDATE: was pointed out I did not have valid HTML. Fixed it following this thread. Still broken.
UPDATE #2: It seems like the problem is .text() - so it completely erases everything? I thought it just replaced the text node, and not all of it's children. I tried .html() but it does the same thing. What method do I use to just replace text then?
UPDATE #3 - one answer suggests I needed a more specific selector. I gave the list item a class of .more-or-less but doing that, it doesn't expand at all.
You'd probably want to use a more strict selector.
In your example case you use .sub-expander to select the node of which you want to replace the text. This matches with the ul.sub-expander however.
Since you want it to replace the text of the li.sub-expander the simplest thing you could do would be to use a more specific selector:
$("li.sub-expander").text("Show Less"); or (better) give the node which contains the text you want to replace another classname, id or other identifier to prevent targeting a different element.

Using javascript append to move multiple divs

I have a wordpress plugin that creates a slider in a output order like this:
<ul>
<li>
<img src="">
<div class="wp1s-caption-wrapper">
<div class="wp1s-caption-title">
<div class="wp1s-caption-content">
</li>
<!-- each slide as this same setup within the li, currently I have three slide so therefore 3 sets of the above li-->
</ul>
Now what I need to do is enter a div (hh-caption) into the div class wp1s-caption-wrapper and then insert wp1s-caption-title and wp1s-caption-content inside that. To do this I though to use append like so:
var j = jQuery.noConflict();
j(document).ready(function(){
j(".wp1s-caption-wrapper").append(j(".hh-caption"));
j(".hh-caption").append(j(".wp1s-caption-title"));
j(".hh-caption").append(j(".wp1s-caption-content"));
});
Now this enters the div hh-caption into each of the wp1s-caption-wrapper divs, but it places every wp1s-caption-title and wp1s-caption-content that appears. I have three sliders with title and captions so what happens is each hh-wrapper contains 3 titles and captions. I only want the title and content directly above each hh-caption (append puts hh-caption below title and content) to move into it not all occurences.
I don't want to edit the plugins codedirectly as any updates would most likely require me to redo everything, I though doing it with javascript in my own pages would be easier to alter if updates changed anything.
Does anyone have any suggestions or point me in the right direction?
I have not really understood what you are trying to do but to my understanding I will ask you to try appendTo instead of append
var j = jQuery.noConflict();
j(document).ready(function(){
j(j('.hh-caption').appendTo('.wp1s-caption-wrapper');
j(j('.wp1s-caption-title').appendTo('.hh-caption');
});
If I understand the question correctly, I think your issue is you are selecting all the elements and then appending your new content. You need to specify the exact element you want to append to. Usually I would say use an id but I understand you don't want to change the existing code so instead you can do 2 things:
One Append: Append everything in one go, I prefer this as its less code, but depending on how complex the html is your adding, this may not be suited.
Multiple Append: Append the caption wrapper, then within that the title etc. This assumes you are always adding it to the end and uses jquery .last()
I hope this helps, sorry its a bit lengthy but wanted to demonstrate it well.
$("#addCaption1").click(function() {
captionImage = "https://images.unsplash.com/photo-1473182446278-fa0683411d10?dpr=1&auto=compress,format&fit=crop&w=1199&h=799&q=80&cs=tinysrgb&crop=";
captionTitle = "This is a new caption";
captionContent = "This caption was added via jquery.";
addCaption1(captionImage, captionTitle, captionContent);
});
$("#addCaption2").click(function() {
captionImage = "https://images.unsplash.com/photo-1462834026679-7c03bf571a67?dpr=1&auto=compress,format&fit=crop&w=1199&h=791&q=80&cs=tinysrgb&crop=";
captionTitle = "This is a new caption";
captionContent = "This caption was added via jquery.";
addCaption2(captionImage, captionTitle, captionContent);
});
function addCaption1(imgUrl, title, content) {
var caption = '<li><img src=' + imgUrl + '><div class="wp1s-caption-wrapper"><div class="wp1s-caption-title"><strong>' + title + '</strong><div class="wp1s-caption-content"><p>' + content + '</p></div></div></div></li>';
$("#sidebar").append(caption);
}
function addCaption2(imgUrl, title, content) {
var captionWrapper = '<li><img src=' + imgUrl + '><div class="wp1s-caption-wrapper"></div></li>';
$("#sidebar").append(captionWrapper);
$(".wp1s-caption-wrapper").last().append('<div class="wp1s-caption-title"><strong>' + title + '</strong></div>');
$(".wp1s-caption-wrapper").last().find(".wp1s-caption-title").append('<div class="wp1s-caption-content"><p>' + content + '</p>');
}
button {
padding: 5px;
}
ul {
list-style: none;
}
li {
margin: 5px;
padding: 5px;
background: #ccc;
color: #333;
width: 180px;
display: inline-block;
}
li img {
width: 180px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<button id="addCaption1">One append</button>
<button id="addCaption2">Multiple appends</button>
<ul id="sidebar">
<!-- Item 1 -->
<li>
<img src="https://images.unsplash.com/photo-1473220464492-452fb02e6221?dpr=1&auto=compress,format&fit=crop&w=1199&h=800&q=80&cs=tinysrgb&crop=">
<div class="wp1s-caption-wrapper">
<div class="wp1s-caption-title"><strong>This is a title for Item 1</strong>
<div class="wp1s-caption-content">
<p>This is some content for Item 1</p>
</div>
</div>
</div>
</li>
<!-- Item 2 -->
<li>
<img src="https://images.unsplash.com/photo-1467791702337-32e58d17bb6d?dpr=1&auto=compress,format&fit=crop&w=1199&h=796&q=80&cs=tinysrgb&crop=">
<div class="wp1s-caption-wrapper">
<div class="wp1s-caption-title"><strong>This is a title for Item 2</strong>
<div class="wp1s-caption-content">
<p>This is some content for Item 2</p>
</div>
</div>
</div>
</li>
<!-- Item 3 -->
<li>
<img src="https://images.unsplash.com/photo-1473496169904-658ba7c44d8a?dpr=1&auto=compress,format&fit=crop&w=1199&h=799&q=80&cs=tinysrgb&crop=">
<div class="wp1s-caption-wrapper">
<div class="wp1s-caption-title"><strong>This is a title for Item 3</strong>
<div class="wp1s-caption-content">
<p>This is some content for Item 3</p>
</div>
</div>
</div>
</li>
</ul>
I recommend running in full page.

Hide/show div in ul with javascript

Let me start by saying I know this is a duplicate, however I couldn't find a solution by looking through previous answers so I was hoping someone can explain what I'm doing wrong with this.
This is part of a menu output by a php script:
<ul id="mtk_main_menu">
<li class="mtk_topmenu" onMouseOver="showMenu('mtk_submenu_0', 'mtk_div_submenu_0');">Manager Options
<div id="mtk_div_submenu_0">
<ul id="mtk_submenu_0">
<li class="mtk_submenu">Preferences</li>
<li class="mtk_submenu">Employee Options</li>
</ul>
</div>
</li>
with the following as my script as per https://stackoverflow.com/a/11842992, which should show each submenu when hovering its parent container
function showMenu(a,b) {
$(a).hover(
function(){
$(b).show();
},
function(){
$(b).hide();
})
}
Javascript and CSS being my weak suits, could someone tell me where my problem is? I feel like onMouseOver doesn't work the way I would expect it to. However I am still learning to manipulate the DOM, please bear with me, thank you!
Edited to reflect missingno's suggestions
For simple scenarios, i'd rather stay away from using JS
Heres how
HTML
<ul id="mtk_main_menu">
<li class="mtk_topmenu" onMouseOver="showMenu('mtk_submenu_0, mtk_div_submenu_0');">Manager Options
<div id="mtk_div_submenu_0">
<ul id="mtk_submenu_0">
<li class="mtk_submenu">Preferences</li>
<li class="mtk_submenu">Employee Options</li>
</ul>
</div>
</li>
CSS
#mtk_main_menu:before,
#mtk_main_menu:after {
content:"";
display:table;
clear:both;
}
#mtk_main_menu {
*zoom:1;
}
#mtk_main_menu > li {
position:relative;
float:left;
}
#mtk_main_menu > li > div {
position:absolute;
left:-999px;
background:grey;
}
#mtk_main_menu > li:hover > div {
left:0;
}
That will do the trick
Fiddle: http://jsfiddle.net/Varinder/7pXSw/
Edit
If you really want to go the JS way - heres how:
HTML
<ul id="mtk_main_menu">
<li class="mtk_topmenu" onMouseOver="showMenu('mtk_submenu_0, mtk_div_submenu_0');">Manager Options
<div id="mtk_div_submenu_0">
<ul id="mtk_submenu_0">
<li class="mtk_submenu">Preferences</li>
<li class="mtk_submenu">Employee Options</li>
</ul>
</div>
</li>
CSS
#mtk_main_menu:before,
#mtk_main_menu:after {
content:"";
display:table;
clear:both;
}
#mtk_main_menu {
*zoom:1;
}
#mtk_main_menu > li {
position:relative;
float:left;
}
#mtk_main_menu > li > div {
position:absolute;
display:none;
/*left:-999px;*/
background:grey;
}
#mtk_main_menu > li:hover > div {
/*left:0;*/
}
JS
function showMenu( args ) {
var arguments = args.split(",");
var submenuWrapper = arguments[1].replace(" ", "");
var $subMenuWrapper = $( "#" + submenuWrapper );
$subMenuWrapper.show();
var $menuItem = $subMenuWrapper.closest("li");
$menuItem.on("mouseout", function() {
$subMenuWrapper.hide();
$(this).off("mouseout");
});
}
Fiddle: http://jsfiddle.net/Varinder/vnwy3/1/
You are calling the event handler with a single string parameter instead of two. Try changing
showMenu('mtk_submenu_0, mtk_div_submenu_0')
into
showMenu('mtk_submenu_0', 'mtk_div_submenu_0')
Additionally, inside your script you should use are using literal strings instead of using your parameters
//This looks for an element of class "a"
$("a").hover(
//This uses the contents of the `a` variable instead:
$(a).hover(
Finally, your function is using 'mtk_submenu_0' as a jquery selector. This searches for a class instead of an id. Change the selector to add a "#" on front or change your jquery logic to not need ids (for example, you could create selectors to search for the first div and ul descendants of the current element.
By doing what you are doing, every time the onMouseOver event is triggered, you're attaching the jQuery hover event. Each time you're attaching another listener.
Instead, initialize your event on document ready:
$(function () {
$("#tk_div_submenu_0").hover(
function(){
$("#mtk_submenu_0").show();
},
function(){
$("#mtk_submenu_0").hide();
})
);
});
That will initialize it when the document is ready, and it will initialize it once.
Then just remove your onMouseOver event from the HTML.
<li class="mtk_topmenu">Manager Options ... </li>
First, you're going the long way around the problem. jQuery has a built in toggle method that performs the show/hide for you. Secondly you're putting the hover call on the child element of the item you're trying to show on hover. Here's an updated version of your code:
<ul id="mtk_main_menu">
<li class="mtk_topmenu" onMouseOver="showMenu(this,'mtk_div_submenu_0');">
Manager Options
<div id="mtk_div_submenu_0">
<ul id="mtk_submenu_0">
<li class="mtk_submenu">Preferences</li>
<li class="mtk_submenu">Employee Options</li>
</ul>
</div>
</li>
</ul>
JS:
function showMenu(a,b) {
var divStr = '#' + a.id + " div";
$(divStr).toggle();
}
I used the hover event on the LI element as it makes more sense in this case.
Here it is in a fiddle: http://jsfiddle.net/3Ecrq/
One thing I find strange about your code is that the first div you mention, mtk_submenu_0, is inside the div you are showing / hiding, mtk_div_submenu_0. Once you hide the outer div, the inner div cannot be 'hovered over', thus preventing it from being shown again.
To ensure the inner div does not get hidden, try something like this:
HTML:
<ul id="mtk_main_menu">
<li class="mtk_topmenu">Manager Options
<div id="mtk_div_submenu_0">
<ul id="mtk_submenu_0">
<li class="mtk_submenu">Preferences</li>
<li class="mtk_submenu">Employee Options</li>
</ul>
</div>
</li>
Javascript:
$(document).ready(function() {
$('.mtk_topmenu').hover(
function() {
$('#mtk_div_submenu_0').show();
},
function() {
$('#mtk_div_submenu_0').hide();
});
});
Because of your line:
<li class="mtk_topmenu" onMouseOver="showMenu('mtk_submenu_0', 'mtk_div_submenu_0');">
I assumed you were looking to have the mtk_div_submenu_0 div show / hide whenever the text Manager Options is moused over. Hopefully this helps!

Making the height of list items in a row follow their largest height

In this page:
http://pastehtml.com/view/1biylhs.html
You can see the book in the center needs more height space than the other two books in the same row because its title has more text.
Currently, each book is contained in a <li> and has a height of 175px.
I can set their height to auto but it still can't make all list items in the same row follow the largest height among them:
http://pastehtml.com/view/1bj19w3.html
What is the simplest way to make the list items in each row follow the largest height of those list items in that row? I have to make it work in IE6 as well.
Many thanks to you all.
I suggest adding some additional markup. Make it a list of lists perhaps.
Something like this
<ul>
<li class="row">
<ul>
<li class="book">
<img />
<h2>title</h2>
</li>
<li class="book">
<img />
<h2>title</h2>
</li>
<li class="book">
<img />
<h2>title</h2>
</li>
</ul>
</li>
<!-- repeat rows here--></ul>
Then some CSS along the lines of
.row{
clear:left;
float:left;
min-height:175px;
}
Note the min-height which allows the height to expand. You will need to feed height to IE6 to achieve the same effect. To do that, you have a lot of options. Conditional comments are one of the standards-compliant choices.
Finally, note that I have used h2 instead of div to contain the book titles. You probably want to give the titles a bit of semantic weight to make them stand out to searches, and so h2 (or any header element) is a better container than div.
I handle it with Javascript:
function resizerows(classname) {
//init some vars
var tablerowlist = new Array();
var maxheight=0;
//get all elements with same class
var rowlist = document.getElementsByTagName("li");
//loop through items and check class name
for (var r=0; r<rowlist.length; r++) {
if (rowlist[r].className.indexOf(classname) != -1) {
//if class name contains classname this is an li we are looking for, add to our array
tablerowlist.push(rowlist[r]);
}//end class name check
}//end loop through all the li elements
//loop through our good list of li elements
for(var l=0; l<tablerowlist.length; l++) {
//dig through the li tag and get all the elements
var childlist = tablerowlist[l].children;
//init a max height
//loop through the child elements (in this case <p> tags)
for(var c=0; c<childlist.length; c++) {
//check to make sure its a P tag
if (childlist[c].tagName == "P") {
//compare height of element to maxheight, if greater maxheight = element height
if (childlist[c].offsetHeight > maxheight) {
maxheight = childlist[c].offsetHeight;
}
}
}//end loop through child elements
//now we have the maxheight loop through all the rows and set the height to max height
for (var ch=0; ch<childlist.length; ch++) {
childlist[ch].style.height = (maxheight) + "px";
}
//reset max height;
maxheight = 0;
}//end loop through the li elements
}
It's a little blunt, you can get more elegant code using a framework like jquery but this has worked for me, cross browser and platform.
Edit:
This is for HTML code formatted thus:
<ul>
<li class="row">
<p class="item">Item 1</p>
<p class="item">Item 2</p>
<p class="item">Item 3, which is really really long text that'll be bigger than the other two items</p>
</li>
<li class="row">
<p class="item">Item 1</p>
<p class="item">Item 2</p>
<p class="item">Item 3, which is really really long text that'll be bigger than the other two items</p>
</li>
This is definitely not easy to do. You talk about rows, but the browser is not seeing rows, it's just seeing wrapped floats.
You could go for tables (not because it's tabular, but just because this layout is very difficult to do without any) but I wouldn't give up that quickly.
As a compromise, I would suggest making each floated block high enough for the image and about three lines of text. Then they each have the same height and line up nicely It's still a guess, but probably the best you can do. .

Categories

Resources