Putting body into a DIV kills the event - javascript

dont ask me why, but I want to put the whole body into a div, and shrink the content. I have this so far:
$('#newButton').click(function() {
if ($('#xxxx').length == 0)
{
$('body').html('<div id="xxxx">'+$('body').html()+'</div>');
$('#xxxx').css('background-color', 'red').css('overflow', 'scroll');
}
alert ($('#xxxx').width());
$('#xxxx').width (parseInt($('#xxxx').width())-10+'px');
});
this is ok so far - but then this click() method never triggers again. For an unknown reason, its killed....

You destroyed the original DOM element when you updated the .html(). Then you created a new element with the same ID, but no event handler. (Remember, HTML isn't the same as the DOM elements. When you remove and replace the HTML, whole new DOM elements are created from that code.)
You could solve this with event delegation:
$('document').on('click','#newButton',function() {
But I would use .wrapAll() instead:
if ($('#xxxx').length == 0) {
$('body > *').wrapAll('<div id="xxxx">');

try this:
<style>
#xxxx{ position: absolute; top: 0px; left: 0px; background: none; display: none; }
</style>
$('#newButton').click(function() {
if ($('#xxxx').length == 0)
{
$('body').append('<div id="xxxx">'+$('body').html()+'</div>');
$('#xxxx').css('background-color', 'red')
.css('overflow', 'scroll')
.css("display", "block";
}
alert ($('#xxxx').width());
$('#xxxx').width (parseInt($('#xxxx').width())-10+'px');
});
this will copy a new "body" (actually it's a div with the same content) on top of the old body.

for those why I want it (it might be weird, but we programmers has to get used to weird solutions).
I wanted to simplify to check the site with shrank windows, so with - and + you can decrease/increase the "window":
$(document).ready(function() {
var doResizing = function (increaseWith)
{
if ($('#xxxx').length == 0)
{
$('body').css('margin', 0).css('padding', 0);
$('body > *').wrapAll('<div id="xxxx" /></div>');
$('#xxxx').css('background-color', 'red').css('overflow', 'scroll').css('padding', 0).css('margin', 0).css('position', 'absolute').width('100%');
}
$('#xxxx').height(parseInt($(window).height())+'px').width(parseInt($('#xxxx').width())+increaseWith+'px');
}
$(document).keypress(function(e) {
if (e.which == 45) { doResizing (-10); }
if (e.which == 43) { doResizing (+10); }
});
});
enjoy!

Related

Two functions on Keypress won't work (textarea)

I have created two function on Enter Key press
to hide show dive on Enter key press
to auto resize textarea Height on Enter when it reached to end.
Here is the fiddle : http://jsfiddle.net/rz3f3gng/2/
$('.one').hide();
$(function() {
//hide show dive on enter press and on other keys hide div
$('#mainContent').on('keypress', function(e) {
if (e.which == 13) {
e.preventDefault();
$('.one').show();
} else {
$('.one').hide();
}
});
function TextareaAuto(o) {
o.style.height = "200px";
o.style.height = (2 + o.scrollHeight) + "px";
}
});
.one {
width: 100px;
height: 30px;
background: red;
}
textarea {
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div class="one">
</div>
<textarea id="mainContent" onkeydown="TextareaAuto(this)" style="overflow:hidden">
</textarea>
Only One function seems to work at time, either Hide show div or auto size textarea.
You should never mix inline event handlers with jQuery handlers. Just use two jQuery handlers or call the function from the existing handler:
e.g.
$('#mainContent').on('keypress', function(e){
if (e.which == 13) {
e.preventDefault();
$('.one').show();
}
else{
$('.one').hide();
}
TextareaAuto(this);
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/rz3f3gng/3/
Update
As you still want the Enter to work (see comment below), just get rid of your e.preventDefault()
e.g.
$('#mainContent').on('keypress', function(e){
if (e.which == 13) {
$('.one').show();
}
else{
$('.one').hide();
}
TextareaAuto(this);
});
http://jsfiddle.net/TrueBlueAussie/rz3f3gng/4/
Which now means it can be reduced using toggle() to
$('#mainContent').on('keypress', function(e){
$('.one').toggle(e.which == 13);
TextareaAuto(this);
});
http://jsfiddle.net/TrueBlueAussie/rz3f3gng/5/

Hide Element on Load, Appear on Hover with Mouseenter

I have a menu that hides/shows child elements with mouseenter & mouseleave, but the child elements appear when the page loads.
I'd like it so the elements don't appear when the page loads but only on mouseenter & mouseleave.
How would I accomplish this with my code below?
$('.side-nav>li.has-flyout', this).on('mouseenter mouseleave', function(e) {
if (e.type == 'mouseenter') {
$('.side-nav').find('.flyout').hide();
$(this).children('.flyout').show();
}
if (e.type == 'mouseleave') {
var flyout = $(this).children('.flyout'),
inputs = flyout.find('input'),
hasFocus = function(inputs) {
var focus;
if (inputs.length > 0) {
inputs.each(function() {
if ($(this).is(":focus")) {
focus = true;
}
});
return focus;
}
return false;
};
if (!hasFocus(inputs)) {
$(this).children('.flyout').hide();
}
}
});
Doing just $('.side-nav>li.has-flyout').hide(); obviously hides the whole nav item. FWIW I'm using Foundation 5's framework.
Ideal: all your initial styles (like display: none) which is what .hide() does should be written in your stylesheet.
/* css */
.side-nav > li.has-flyout .flyout {
display: none;
}
Less ideal:
// JavaScript
$('.side-nav>li.has-flyout').children('.flyout').hide();
Through CSS, if your sub element list has class e.g. .sub-menu
then just add css
.sub-menu { display: none; }

Click outside element without using event.stopPropagation

I know there are lots of ways to detect the click outside of an element. Mostly all of them use event.stopPropagation. Since event.stopPropagation will break other stuff, I was wondering if there is another way to achieve the same effect. I created a simple test for this:
HTML:
<div class="click">Click me</div>
Javascript:
$(function() {
var $click = $('.click'),
$html = $('html');
$click.on( 'click', function( e ) {
$click.addClass('is-clicked').text('Click outside');
// Wait for click outside
$html.on( 'click', clickOutside );
// Is there any other way except using .stopPropagation / return false
event.stopPropagation();
});
function clickOutside( e ) {
if ( $click.has( e.target ).length === 0 ) {
$click.removeClass('is-clicked').text('Click me');
// Remove event listener
$html.off( 'click', clickOutside );
}
}
});
http://jsfiddle.net/8p4jhvqn/
This works, but only because i stop the bubbling with event.stopPropagation();. How can i get rid of event.stopPropagation(); in this case?
It can be done in a simpler way, can't it be? Why complicate things when something as simple as below could work.
$(document).click(function(e){
var elm = $('.click');
if(elm[0] == e.target){
elm.addClass("is-clicked").text("click outside");
} else { elm.removeClass("is-clicked").text("click inside"); }
});
DEMO
You could do something like this to achieve the same effect
$(document).on("click", function(e){
var target = $(e.target);
if(target.hasClass("click")){
$click.addClass('is-clicked').text('Click outside');
}else{
$click.removeClass('is-clicked').text('Click me');
}
});
HTML code:
<div id="box" style="width:100px; height:100px; border:1px solid #000000; background-color:#00ff00;"></div>
JavaScript code:
function Init()
{
$(document).click(function(event){
if(event.target.id == "box")
{
$(event.target).css("backgroundColor", "#ff0000");
}
else
{
$("#box").css("backgroundColor", "#00ff00");
}
})
}
$(document).ready(Init);
If the element in question has child elements, then those may show up as e.target, and you can't simply compare it to your element.
In that case, capture the event in both the event and in the document, and detect events which only occurred on the document, for example by recording and comparing e.target:
var lastTarget = undefined;
$("#interesting-div").click(function(e) {
// remember target
lastTarget = e.target;
});
$(document).click(function(e) {
if (e.target != lastTarget) {
// if target is different, then this event didn't come from our
// interesting div.
// do something interesting here:
console.log("We got a click outside");
}
});
var lastTarget = undefined;
$("#interesting-div").click(function(e) {
// remember target
lastTarget = e.target;
});
$(document).click(function(e) {
if (e.target != lastTarget) {
// if target is different, then this event didn't come from our
// interesting div.
// do something interesting here:
console.log("We got a click outside");
}
});
#interesting-div {
background: #ff0;
border: 1px solid black;
padding: .5em;
}
#annoying-childelement {
background: #fa0;
border: 1px solid black;
margin: 1em;
padding: .5em;
width: 20em;
}
#large-div {
background: #ccc;
padding: 2em 2em 20em 2em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="large-div">
<div id="interesting-div">
This is our interesting element
<div id="annoying-childelement">
child element
</div>
</div>
</div>
</div>

Eventlistener for an input "form"?

Here is a common problem I have no good way to handle:
On a web page I display an input form when a button is clicked. Behind the form I put a background that dim the page:
var bgDiv = document.createElement("div");
var formDiv = document.createElement("div");
bgDiv.appendChild(formDiv);
document.body.appendChild(bgDiv);
With ids and css something like this:
#zreaderwp-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(30, 30, 30, 0.7);
z-index: 100;
}
#zreader-form {
margin-top: 100px;
margin-left: auto;
margin-right: auto;
width: 600px;
height: 600px;
background: white;
}
I attach an eventlistener to bgDiv in a hope that ESC should hide the form:
bgDiv.addEventListener("keydown", function(ev) {
ev.stopPropagation();
console.log("ev.keyCode", ev.keyCode);
if (27 == ev.keyCode) {
console.log("was 27");
bgDiv.style.display = "none";
}
});
To my disappointment this never works as I expect it to. The reason is of course that focus is not always inside bgDiv (though it visually seems to be that).
Is there a good and simple way to handle this?
UPDATE: I created a fiddle to illustrate the problem a bit better, http://jsfiddle.net/lborgman/Musqs/1/. As can be seen there moving the event handler does not solve the problem.
If you want to hide bigDiv whenever esc is pressed globally, add the event listener to document itself (though stopPropagation() could cause some issues, so I've moved it inside the if statement, modified for accuracy.):
document.addEventListener("keydown", function(ev) {
console.log("ev.keyCode", ev.keyCode);
if (27 == ev.keyCode && bgDiv.style.display !== "none") {
ev.stopPropagation();
console.log("was 27");
bgDiv.style.display = "none";
}
});
Additionally, use triple equals === in your if statement. It probably won't change much here, but is a good habit to get into. For more details, see: == vs ===
Fiddle answer here.
FIDDLE
Attach it to document instead and don't stop the event from propogating when its a key press inside the input.
Here you go
var docInp = document.getElementById("doc-input");
docInp.focus();
docInp.addEventListener("keydown", function(ev){
if (27 !== ev.keyCode && bgDiv.style.display !== "none") {
// we won't stop event propogation if the ESC key was pressed and the bgDiv was visible
ev.stopPropagation();
}
alert("Hi, it's me, docInp? Did u want something?");
});
var bgDiv = document.createElement("div");
bgDiv.setAttribute("id","bg");
var formDiv = document.createElement("div");
formDiv.setAttribute("id","form");
bgDiv.appendChild(formDiv);
formDiv.appendChild(document.createTextNode("Press ESC!"));
document.body.appendChild(bgDiv);
document.addEventListener("keydown", listenToKeys, false);
function listenToKeys(ev) {
ev.stopPropagation();
if (27 == ev.keyCode) {
bgDiv.style.display = "none";
document.removeEventListener("keydown", listenToKeys, false);
}
}
I am a bit hesitating to answer my own question, but since others may face the same problem it looks most helpful to me. After chatting with #SomeKittens Ux2666 yesterday and struggling with the problem that focus will not stay inside "bg" I found a solution that I think is reasonable, see http://jsfiddle.net/lborgman/Musqs/31/.
This solution uses "focusout" (which is unfortunately not supported by FF at the moment, see https://developer.mozilla.org/en-US/docs/Web/Reference/Events/focusout):
bgDiv.addEventListener("focusout", function(ev){
// Fix-me: Won't currently work in FF, but it is not
// really essential and FF will probably support this
// later.
ev.stopPropagation();
var to = ev.relatedTarget;
var from = ev.target;
console.log("bg got focusout, to", to, "from", from);
var inBg = false;
n = 0;
while (!inBg && to && n++ < 5) {
to = to.parentNode;
inBg = to === bgDiv;
}
if (!inBg) {
console.log("trying to keep focus in bg");
from.focus();
}
});
The other half of the solution is where to add the ESC keydown eventlistener. That has been addressed in the answer by #SomeKittens and #Lucky Soni, thanks. (But the eventlistener may actually stay on "bg" when the problem with focus has been solved!)

After initial click, have to click twice with Jquery click()

Couldn't find a solution that actually worked, but I want that on a click, a div shows.
Now this works when I load the page, but then after that first click, I have to click twice every time for the div to show.
Any ideas?
$(document).ready(function () {
setMenu();
});
function setMenu()
{
var headerExtIsOpen = false;
$('#headerExt').hide();
$('#header').click(function () {
if (!headerExtIsOpen) {
$('#headerExt').show();
headerExtIsOpen = true;
} else {
$('#headerExt').hide();
headerExtIsOpen = false;
}
});
}
There is no need to remember the state, just use toggle()
$(function () {
setMenu();
});
function setMenu()
{
$('#headerExt').hide();
$('#header').on("click", function (e) {
e.preventDefault();
$('#headerExt').toggle();
});
}
You said you want to toggle other things.
Best thing would be to toggle a class to change the color
$('#header').on("click", function (e) {
e.preventDefault();
$(this).toggleClass("open");
$('#headerExt').toggle();
});
another way is to check the state
$('#header').on("click", function (e) {
e.preventDefault();
var child = $('#headerExt').toggle();
var isOpen = child.is(":visibile");
$(this).css("background-color" : isOpen ? "red" : "blue" );
});
if the layout is something like
<div class="portlet">
<h2>Header</h2>
<div>
<p>Content</p>
</div>
</div>
You can have CSS like this
.portlet h2 { background-color: yellow; }
.portlet > div { display: none; }
.portlet.open h2 { background-color: green; }
.portlet.open > div { display: block; }
And the JavaScript
$(".portlet h2 a").on("click", function() {
$(this).closest(".portlet").toggleClass("open");
});
And there is layouts where it would be possible to have zero JavaScript involved.
Turns out I had some script hidden in my .js file that closes the menu again when the user clicks elsewhere, that I forgot about.
function resetMenu(e) {
var container = $('#headerExt');
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
$('#header').css("background-color", "inherit");
container.hide();
headerExtIsOpen = false;
}
}
I forgot to set the headerExtIsOpen back to false again after closing it in this function (code above shows the fix). Now it works fine :)

Categories

Resources