How do get a parent node without having a starting reference point? - javascript

I want to provide a method for my web app that allows a user to call my function anywhere within his code (inside script tags) that will display a fade-in/fade-out message. What I don't know how to do is determine where I am at in the DOM without having a starting reference point.
Function:
function displayMessage(message) {
// Display a notification if the submission is successful.
$('<div class="save-alert">' + message + '</div>')
.insertAfter($('')) // Do not know how to know where to put the message.
.fadeIn('slow')
.animate({ opacity: 1.0 }, 2000)
.fadeOut('slow', function () {
$(this).remove();
});
}
The HTML:
<!-- Some HTML -->
<div>
<script type="text/javascript">
displayMessage("My message.");
</script>
</div>
<!-- Some more HTML. -->

There isn't any reliable way to get this information. You should do what most other libraries do -- have the user pass in the ID or reference of an element to your displayMessage function so that you can use it.

Generally there are two ways, one, as Casablanca noted you provide a div in the DOM to append the message. Second, and this is the one I think you want, is to create a div node on the DOM itself. That way you have total control. After all it is going to fade out when you're done. Might as well kill it from the dom as well. This is what fancylightbox (and I am sure many others) does with the DOM. You'd want to place it right at the beginning of the body, so there are no other containing elements, and style it as you wish - likely so it floats somewhere near the middle of the page like a lightbox/dialog box.

Unless you want to take in a css selector or an id as argument there is also an alternative to create a jQuery widget. This way you can use it like:
$("#msgArea").messageWidget("displayMessage");
or even reuse it many times
$("#msgArea").messageWidget("displayMessage", "message to display");
Sample boilerplate widget:
(function( $ ) {
$.widget("ui.messageWidget", {
// default options
options: { message: undefined},
// constructor
_create: function() {
if (this.options.message !== undefined) {
this.displayMessage(this.options.message);
}
},
// Displays the message
displayMessage: function() {
this.element.append("your div's and messages");
},
// Allows constructor/setter arguments
_setOption: function( key ) {
$.Widget.prototype._setOption.apply( this, arguments );
}
});
}(jQuery));

You probably want to use fixed positioning to put your box at a place relative to the browser's viewport, regardless of where a user has scrolled to in the document.

Related

Prevent Ajax from triggering jQuery script

We got a simple jQuery script on Drupal site that injects a div class with content:
(function($) {
Drupal.behaviors.myHelpText = {
attach: function (context, settings) {
//code starts
//change placeholder text
$('.form-item-quantity').append('<span class="help-block">For orders over 10 call for volume pricing</span>');
$('.help-block').css("flex-basis", "100%");
//code ends
}
};
})(jQuery);
The page has Drupal Commerce and various product attribute fields that gets processed by Ajax every time selecting an attribute. And when doing that our script injects same duplicate line each time on Ajax load/update.
How to avoid that? We just want jQuery code work once on page load.
Only add the element if it doesn't exist, otherwise do nothing.
(function($) {
Drupal.behaviors.myHelpText = {
attach: function (context, settings) {
if (!document.getElementById('help')) {
$('.form-item-quantity').append(
'<span id="help" class="help-block">For orders over 10 call for volume ricing</span>'
);
$('.help-block').css("flex-basis", "100%");
}
}
};
})(jQuery);
You have to understand that drupal.behaviors fire on page load and when ajax returns results. It is designed this way because you may want your code to run again on the ajax results, for example, if you are updating part of the page via ajax and it needs event listeners applied, or a class added.
The context variable is the key here.
on first page load, the context will be the whole window, but when ajax returns the result, the context will just be what is returned by the ajax.
Knowing this, you should be using context in your jquery selectors.
eg.
(function($) {
Drupal.behaviors.myHelpText = {
attach: function (context, settings) {
//code starts
//change placeholder text
$('.form-item-quantity', context).append('<span class="help-block">For orders over 10 call for volume pricing</span>');
$('.help-block', context).css("flex-basis", "100%");
//code ends
}
};
})(jQuery);
For added protection against something processing multiple times, you can use jquery once(), but this is usually not needed if using the context variable in the selector. jQuery once() separate library that must be loaded.
Why don't use jQuery once? My thought — it's a classic approach. A bunch of examples lives in docs on drupal.org
$('.form-item-quantity').once('help-appended').append('<span class="help-block">For orders over 10 call for volume pricing</span>');
And I'm not sure you need to apply styles via js. A css file is a better place for it. And jquery once should be available in your environment. That's it.

casperjs: how do I click a remote div and then update it's class name?

As a way of learning CasperJS, I am trying to initiate a click event on a div on a remote page, and then change the class name of the div after I have clicked it. The idea is to find the first clickable div, click it, and then mark it as clicked so I can skip over it to other clickable divs. The markup for the div tag on the remote page looks like:
<div class='clickable_div'></div>
I have tried the following casperjs code:
...
casper.then(function() {
if( this.exists( 'div.clickable_div' ) ) {
this.evaluate(function() {
this.click(document.querySelector('div.clickable_div'));
return document.querySelector('div.clickable_div').setAttribute("className","clicked");
});
}
});
...
It doesn't seem to work. First, I don't think I am initiating the mouse click event on the div correctly. What am I missing? Second, when I fetch the updated html, I don't see any changes in the div's class name. Am I going about this step in the wrong way?
You're calling this.click within evaluate(), it just can't work as evaluate() executes code within the page DOM context where there's probably no window.click method.
Here's a possibly working script:
var linkSelector = 'div.clickable_div';
casper.then(function() {
if (!this.exists(linkSelector)) return;
this.click(linkSelector);
this.evaluate(function(linkSelector) {
__utils__.findOne(linkSelector).setAttribute("className", "clicked");
}, linkSelector);
});
You may want to have better handling of errors and edge cases, but you get the idea.

How to hide all text, which are smaller than certain pixel and bring them back later using Javascript?

I'm working on a project, which in some cases requires to hide all small text (eg. less than 12px), and on some other event, bring them back. It's not a website, but something happening on the webkit browser. I don't have control over the content on the page (developed by the third party developers), but have control to modify it. I know I can loop through all tag elements and check font sizes and hide them if smaller than 12px, but it's not only inefficient, but the text can be changed to be shown again, say after an ajax call, which is "prohibited". Other solution would be to run that loop every couple seconds, but it's an expensive process.
Other task is to show small text on some other event, which is not too difficult to implement by just using simple custom class.
You can run the code on page-load, and then when any AJAX call completes using jQuery's Global AJAX Event Handlers: http://api.jquery.com/category/ajax/global-ajax-event-handlers/
$(function () {
function findSmallText($root, state) {
if (typeof $root == 'undefined') {
$root = $(document);
}
if (typeof state == 'undefined') {
state = 'none';
}
$.each($root.find('p, div, span, font, button, a'), function () {
if ($(this).css('font-size').replace(/(px|pt|em)/gi, '') <= 12) {
$(this).css('display', state);
}
});
}
//run the function when the DOM is ready
findSmallText();
//also run the function when any AJAX request returns successfully
$(document).ajaxSuccess(findSmallText);
});
You can pass the findSmallText function two arguments:
$root: (jQuery object) the root element to start looking for small text, limit this as much as possible to increase performance (so unnecessary elements don't have to be scanned).
state: (string) the display property to add to the elements with small text, you can use block to show the elements.
if the HTML structure doesnt change (no extra containers added thru AJAX) simply analyze the page onLoad (kinda like what Jasper suggests) but instead of re-running the analysis after each AJAX call you add a new class - let's call it .HideMeInCertainCases for the fun of it. That way you can hide / show everything you want with a simple selector whenever you want.
So instead of this line: $(this).css('display', state); use $(this).addClass('HideMeInCertainCases');
When the event you were talking about occurs you can then toggle the display state with this selector $("HideMeInCertainCases").toggleClass("hideMe"). Changing the display-attribute directly might break your layout as the nodes containing text might have different displays to begin with (block, inline, inline-block...). Of course .hideMe { display:none; } should be somewhere in your stylesheet. If you want the layout to stay the same and only hide the content use visibility instead of display

Changing image source with Jquery

Using Jquery, I've managed to make a dropdown login form triggered by clicking a button. However, I am also trying to change the direction of the arrow next to it by replacing the src image, and it appears to do nothing.
$("#login_panel").slideToggle(200).toggle(
function() { $("#arrow").attr('src', '/src/east.gif';) },
function() { $("#arrow").attr('src', '/src/south.gif';) }
);
This can be seen at:
http://dev.mcmodcenter.net (The 'Login' button)
$(document).ready(function() {
$("#login_panel").slideToggle(200).toggle(
function() { $("#arrow").attr('src', '/src/east.gif';) },
function() { $("#arrow").attr('src', '/src/south.gif';) }
);
for (var i = 0; i < 2; i++) {
$(".mod").clone().insertAfter(".mod");
}
$(".mod").lazyload({
effect: "fadeIn"
});
});
You can directly access this.src - no need to create a new jQuery object for that:
$('#arrow').toggle(
function() { this.src = '/src/south.gif'; },
function() { this.src = '/src/east.gif'; }
);
And if you prefer to do it via .attr() at least use $(this) (DRY - don't repeat yourself - in this case, don't specify the selector more often than necessary)
$("#arrow").toggle(
function(){$("#arrow").attr("src", "/src/south.gif");},
function(){$("#arrow").attr("src", "/src/east.gif");}
);
You left off the "#" in the handler functions. By just referring to "arrow", you were telling jQuery to look for (presumably absent) <arrow> tags.
Now, as to the larger situation, what you're setting up there is something that'll make the image change when the image itself is clicked. Your description of your goal makes me think that that's not quite what you want, but it's hard to tell. If you want some other element to control the changes to the image, then you'd attach the handler(s) elsewhere.
Is the image you want to change that little black arrow next to the login button? If so, then what should happen is that the code to set the image should be added to the existing handler that slides the login form up and down. (By the way, in Chrome the login box shows up in what seems like an odd place, far to the left of the button.)
looks like you forget to put the # before the arrow in $("arrow")
it should be like this
$("#arrow").toggle(
function(){$("#arrow").attr("src", "/src/south.gif");},
function(){$("#arrow").attr("src", "/src/east.gif");}
);
$("arrow") will match <arrow>, you lost the #
Also, the toggle method does not take two functions as its arguments, it works in a completely different way to what you are trying to do with it. Yes, it does, there are two different toggle methods for jQuery (insert rant about awful API design)
And now you have completely edited the code…
Your code now immediately assigns strings to the this.src (where this is (I think) the document object), and then passes those two strings as arguments to the toggle method (which are not acceptable arguments for it)
And now you have completely edited it again…
This code should work:
$('#login_button').click(function() {
$(this).find('#arrow').attr('src', function(i, v) {
return v.indexOf('east.gif') < 0 ? '/src/east.gif' : '/src/south.gif';
});
$('#login_panel').slideToggle(200);
});

jQuery code critque

Thought I'd post here. My first hour on jQuery, actually first programing ever done. Would love to learn whats not right and how it could be better.
$(function() {
function hide_me()
//A place to specify which elements you want hidden, on page load.
{
$("li.credentials").hide();
}
function first_bow()
//The div right_column takes a bow on initial load.
{
$("div#right-column").show("drop");
}
function bigpeek()
//The third column toggles in/out. All elements under div right_column.
{
$("div#right-column").toggle("drop", "fast");
}
function smallpeek()
//Smaller snippets like credentials or user assignment flying in/out.
{
$("li.credentials").toggle("drop", "fast");
}
$(document).ready(function() {
$("*").ready(hide_me);
$("*").ready(first_bow);
$(".btn-new-email").click(bigpeek);
$(".button").click(smallpeek);
$(".icon-delete").mouseover(function() {
$(this).effect("bounce", "fast");
});
});
});
The best thing to learn about programming is how to effectively re-use code. In your code, you have set up some functions that you yourself claim will do a bunch of the same thing. So instead, you could make it better by only writing code to do the repeated task once.
For one example, instead of creating a function where you place a bunch of things that need to be hidden, I would add a class to the elements that should be hidden, and then hide all those elements:
function hide_me()
//Hides anything with the "hide-me-onload" class
{
$(".hide-me-onload").hide();
}
$(function () {
...
}
is the same as
$(document).ready(function() {
...
}
So you can move the method calls from inside your $(document).ready() to be inside your $(function(){}). Also try to use IDs instead of class names wherever possible. Something like this will go through the entire DOM to look for an element
$(".item")
Be more specific
$("#itemID") // use IDs instead of Classes
//If you have to use class name then you can speed up the selector by adding the element tag before it
$("div.item")
Using $("*").ready() within $(document).ready() is redundant... you already know using all of the elements are ready! Also, in general using the universal selector $('*') is very inefficient.
So, the first two lines of your $(document).ready() can just be:
hide_me();
first_bow();
Other than that and a couple of issues with organization and nomenclature you're off to a great start, keep it up!

Categories

Resources