Stuck in understanding Bootstrap.v3 tab.js source code - javascript

Background
Recently I plan to write my own js lib which mainly refer to Bootstrap v3. I stuck in some code, please help.
Problem
In Bootstrap v3 tab.js, I have no idea what's the effect about two segments below
Segment1:
// line 39 - 47
var hideEvent = $.Event('hide.bs.tab', {
relatedTarget: $this[0]
})
var showEvent = $.Event('show.bs.tab', {
relatedTarget: $previous[0]
})
$previous.trigger(hideEvent)
$this.trigger(showEvent)
Segment2:
// line 55 - 62
$previous.trigger({
type: 'hidden.bs.tab',
relatedTarget: $this[0]
})
$this.trigger({
type: 'shown.bs.tab',
relatedTarget: $previous[0]
})
My Try
Withdraw the single tab.js in my script and it works, which means two segmens above not rely on other widget/util (expect the transition effect)
I delete two segments in tab.js and it works still. I have no idea what's the purpose of these two segments?
I review the jQ API: $.Event & trigger, what i can got is two segments define some custom function and trigger.
Help
What's the purpose of these two segments? Thanks a lot.

Bootstrap has created a series of tools that it wants to make as extensible as possible. This means, that they want to allow the developer using their tools to "listen" to a number of "Events" and perform follow up actions.
The two listed in your question allow the developer to "hook" into either the "show" event or "hide" event. (There are actually 2 more provided by bootstrap ["shown" and "hidden"]). From the Documentation these events are fired when:
show.bs.tabThis event fires on tab show, but before the new tab has been shown. Use event.target and event.relatedTarget to target the active tab and the previous active tab (if available) respectively.
shown.bs.tabThis event fires on tab show after a tab has been shown. Use event.target and event.relatedTarget to target the active tab and the previous active tab (if available) respectively.
hide.bs.tabThis event fires when a new tab is to be shown (and thus the previous active tab is to be hidden). Use event.target and event.relatedTarget to target the current active tab and the new soon-to-be-active tab, respectively.
hidden.bs.tabThis event fires after a new tab is shown (and thus the previous active tab is hidden). Use event.target and event.relatedTarget to target the previous active tab and the new active tab, respectively.
An example of how this might be used could be the case of "When the user clicks on a tab, stop playing the youtube video in the previous tab by refreshing its src". The code for something like this could be
$('#myTabs').on('show.bs.tab', function(event){
// previous tab
if(event.relatedTarget){
var youtubeIframe = $(event.relatedTarget).find('iframe');
var src = youtubeIframe.attr('src');
youtubeIframe.attr('src', 'about:blank');
youtubeIframe.attr('src', src);
}
});
Alternatively, you could target the tab that was just "hidden". Rather than looking to the current tab and using its relatedTarget we would use the hide.bs.tab event and use event.target, like so:
$('#myTabs').on('hide.bs.tab', function(event){
// current tab
if(event.target){
var youtubeIframe = $(event.target).find('iframe');
var src = youtubeIframe.attr('src');
youtubeIframe.attr('src', 'about:blank');
youtubeIframe.attr('src', src);
}
});
NB: Im sure there are better ways to stop playing youtube videos. This is used as example code.

Related

Handle Click Event Listener in Window Scroll Event

I applied window scroll event and then I am getting elements with document.querySelectorAll. So, After executing this i have NodeList. I am looping over this nodelist with forEach.
In forEach i am triggering click event on item. But my click event trigger multiple time. How I can handle it.
Note:
Run this code on vimeo home page.
https://vimeo.com/home
window.addEventListener("scroll",()=>{
var config = document.querySelectorAll("[data-config-url]");
config.forEach((item)=>{
item.addEventListener("click",(e)=>{
var configData = e.currentTarget.getAttribute("data-config-url");
console.log(configData);
});
});
});
I want log configData of item for single time.
As stated on my comments above, I couldn't hit the scenario you described at the url https://vimeo.com/home. Not even when adopting the mobile screen simulation. I'm using Firefox 101.0.1 (64 bit) on Windows 10 in this moment.
document.querySelectorAll("[data-config-url]").length always returns zero and that's true also after trying to navigate the page, scrolling, opening menu blocks and so on. I didn't login because I don't have an account and you didn't specify how the page is supposed to be visited beyond the url itself.
Anyway when the scenario gets hit, that's the code implementing the logic I suggested in my comments. I just slightly modified your code so that the handler gets added only if the element wasn't already processed previously.
It then repeats the operation once every 500ms:
setInterval(scanPageAndAddHandlers, 500);
function scanPageAndAddHandlers(){
console.log('scanning...');
var config = document.querySelectorAll("[data-config-url]");
config.forEach((item)=>{
if (item.dataset.handlerWasAdded != 'true'){
//add the listener for the click event to the item element
item.addEventListener("click",(e)=>{
var configData = e.currentTarget.getAttribute("data-config-url");
console.log(configData);
});
//mark this item as processed
item.dataset.handlerWasAdded = 'true';
}
});
}

e.preventDefault() and e.stopPropagation() not working as expected

The app in question is basically a DOM inspector tool similar to the one available in Chrome Dev Tools made using plain JS. Basically when the user clicks on an element attributes such as class names, xpaths and texts should be available to the user. I have run into a problem which is: When a user clicks on an element such as a link or a button, the browser navigates to the intended page. I have tried to prevent this problem using the following piece of code:
var target = getSelectedElementFromPoint(e.clientX, e.clientY); // target has the element the user wants to inspect
if(target.nodeName.toLowerCase() == "a" || target.nodeName.toLowerCase() == "button"){
e.stopPropagation(); // e is the event
e.preventDefault();
}
The intention of the code is to get the element the user clicks on, check if it is an anchor tag or a button and if so, stop the event from progressing further. However, it does not work as expected as, on mouse click, it still navigates to the page.
Any help would be appreciated.
EDIT: Please note that I cannot use jQuery in this particular instance. Thanks
EDIT2: Probably should have mentioned this earlier but I forgot.
I have actually tried return false as well. Using return false achieves the required functionality but the only problem being, once the user leaves the "Inspect mode" that link is no longer clickable and that is not desirable behavior.
Basically what I want is that when the user enters the "Inspect mode" for all links to be unclickable. And these links should be clickable once "Inspect mode" is removed. Rather than disable all links on the page, I thought of disabling the one the user clicked on.
The solution is to simply wrap your code in a check for 'inspectMode'. 'inspectMode' should be a boolean var.
Add the foloowing code to your eventhandler:
if (inspectMode)
{
var target = getSelectedElementFromPoint(e.clientX, e.clientY);
if(target.nodeName.toLowerCase() == "a" || target.nodeName.toLowerCase() == "button")
{
e.stopPropagation(); // e is the event
e.preventDefault();
return false;
}
}
return true;

Controlling dijit.MenuBar with MouseOver events

In Dojo, is it possible to configure dijit.MenuBar so that the menus are triggered by MouseOver and MouseOut events? Actually this behavior is available already, but it is switched on or off by initial or successive mouse click events - so initially, MouseOver would not cause menu popup, but if the user clicks on a menu item, the menubar then becomes responsive to MouseOver events. A successive mouse click would again switch off this behavior.
What I would like to have is menus and sub-menus popping up based on MouseOver events without interference from click events. Please check the examples at http://dojotoolkit.org/reference-guide/dijit/MenuBar.html to see what I mean.
Your question piqued my interest enough to make a working solution.
I checked the dijit._MenuBase source code at dijit/Menu.js and apparently there is a this.isActive flag that is checked before proceeding. So I created a subclass that just sets this flag as true beforehand:
_ActivateOnMouseoverMixin = dojo.declare(null, {
onItemHover: function(item){
if(!this.isActive){
this._markActive();
}
this.inherited(arguments);
}
});
ActiveMenuBar = dojo.declare([dijit.MenuBar, _ActivateOnMouseoverMixin], {});
As a bonus, you can also modify the delay with the popupDelay variable (I changed it to be faster in the example)
I have no idea if there is another, more sane, way to do the same thing.
Here is an example that extends the solution of 'Hugomg' to case of unhovering the menu and the sub-menu:
[enter link description here][1]
[1]: http://jsfiddle.net/vg10c9md/2/

how to get intitial triggering of window.onBind on page load in Chrome like in Firefox

i have that problem: i need to have a variable set to false/true depending on whether the page is loaded in the current tab or in an inactive tab. so i tried to do it with the focus-event, more or less like this (it's jquery):
var hasFocus = false;
$(function() {
$(window).focus(function() {
hasFocus = true;
});
});
firefox and ie it do what i want: if the page is loaded in the active tab the event is triggered immediately, loaded in a background tab the event is only triggered when the tab gets active.
in chrome however the event does not get triggered when the page is loaded in the current active tab. does anybody know a workaround for this? i also tried events like mouseenter, hover but unfortunately they get executed on pageload in an inactive tab too... thanks in advance!
A tricky way would be this.
setInterval/setTimeout is only fired once a second at most for inactive tabs in Chrome. So, you could set an interval (or timeout) to be run after e.g. 10ms. If it only runs after a much longer time (e.g. 1 second), the page must be inactive. Otherwise, it would be run in 10ms (like you set).
I woulds suggest that you try mousemove as an event -- e.g.
var humanHasInteracted = false;
$(function() {
$(window).mousemove(function() {
humanHasInteracted = true;
});
});
alternatively use bind/unbind so that the event handler can removed when the first mousemovement is detected.

Can I call jQuery's click() to follow an <a> link if I haven't bound an event handler to it with bind or click already?

I have a timer in my JavaScript which needs to emulate clicking a link to go to another page once the time elapses. To do this I'm using jQuery's click() function. I have used $().trigger() and window.location also, and I can make it work as intended with all three.
I've observed some weird behavior with click() and I'm trying to understand what happens and why.
I'm using Firefox for everything I describe in this question, but I am also interested in what other browsers will do with this.
If I have not used $('a').bind('click',fn) or $('a').click(fn) to set an event handler, then calling $('a').click() seems to do nothing at all. It does not call the browser's default handler for this event, as the browser does not load the new page.
However, if I set an event handler first, then it works as expected, even if the event handler does nothing.
$('a').click(function(){return true;}).click();
This loads the new page as if I had clicked the a myself.
So my question is twofold: Is this weird behavior because I'm doing something wrong somewhere? and why does calling click() do nothing with the default behavior if I haven't created a handler of my own?
As Hoffman determined when he tried to duplicate my results, the outcome I described above doesn't actually happen. I'm not sure what caused the events I observed yesterday, but I'm certain today that it was not what I described in the question.
So the answer is that you can't "fake" clicks in the browser and that all jQuery does is call your event handler. You can still use window.location to change page, and that works fine for me.
Another option is of course to just use vanilla JavaScript:
document.getElementById("a_link").click()
Interesting, this is probably a "feature request" (ie bug) for jQuery. The jQuery click event only triggers the click action (called onClick event on the DOM) on the element if you bind a jQuery event to the element. You should go to jQuery mailing lists ( http://forum.jquery.com/ ) and report this. This might be the wanted behavior, but I don't think so.
EDIT:
I did some testing and what you said is wrong, even if you bind a function to an 'a' tag it still doesn't take you to the website specified by the href attribute. Try the following code:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
<script>
$(document).ready(function() {
/* Try to dis-comment this:
$('#a').click(function () {
alert('jQuery.click()');
return true;
});
*/
});
function button_onClick() {
$('#a').click();
}
function a_onClick() {
alert('a_onClick');
}
</script>
</head>
<body>
<input type="button" onclick="button_onClick()">
<br>
<a id='a' href='http://www.google.com' onClick="a_onClick()"> aaa </a>
</body>
</html>
It never goes to google.com unless you directly click on the link (with or without the commented code). Also notice that even if you bind the click event to the link it still doesn't go purple once you click the button. It only goes purple if you click the link directly.
I did some research and it seems that the .click is not suppose to work with 'a' tags because the browser does not suport "fake clicking" with javascript. I mean, you can't "click" an element with javascript. With 'a' tags you can trigger its onClick event but the link won't change colors (to the visited link color, the default is purple in most browsers). So it wouldn't make sense to make the $().click event work with 'a' tags since the act of going to the href attribute is not a part of the onClick event, but hardcoded in the browser.
If you look at the code for the $.click function, I'll bet there is a conditional statement that checks to see if the element has listeners registered for theclick event before it proceeds. Why not just get the href attribute from the link and manually change the page location?
window.location.href = $('a').attr('href');
Here is why it doesn't click through. From the trigger function, jQuery source for version 1.3.2:
// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
event.result = false;
// Trigger the native events (except for clicks on links)
if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) {
this.triggered = true;
try {
elem[ type ]();
// Prevent Internet Explorer from throwing an error for some hidden elements
}
catch (e)
{
}
}
After it calls handlers (if there are any), jQuery triggers an event on the object. However it only calls native handlers for click events if the element is not a link. I guess this was done purposefully for some reason. This should be true though whether an event handler is defined or not, so I'm not sure why in your case attaching an event handler caused the native onClick handler to be called. You'll have to do what I did and step through the execution to see where it is being called.
JavaScript/jQuery doesn't support the default behavior of links "clicked" programmatically.
Instead, you can create a form and submit it. This way you don't have to use window.location or window.open, which are often blocked as unwanted popups by browsers.
This script has two different methods: one that tries to open three new tabs/windows (it opens only one in Internet Explorer and Chrome, more information is below) and one that fires a custom event on a link click.
Here is how:
HTML
<html>
<head>
<script src="jquery-1.9.1.min.js" type="text/javascript"></script>
<script src="script.js" type="text/javascript"></script>
</head>
<body>
<button id="testbtn">Test</button><br><br>
Google<br>
Wikipedia<br>
Stack Overflow
</body>
</html>
jQuery (file script.js)
$(function()
{
// Try to open all three links by pressing the button
// - Firefox opens all three links
// - Chrome only opens one of them without a popup warning
// - Internet Explorer only opens one of them WITH a popup warning
$("#testbtn").on("click", function()
{
$("a").each(function()
{
var form = $("<form></form>");
form.attr(
{
id : "formform",
action : $(this).attr("href"),
method : "GET",
// Open in new window/tab
target : "_blank"
});
$("body").append(form);
$("#formform").submit();
$("#formform").remove();
});
});
// Or click the link and fire a custom event
// (open your own window without following
// the link itself)
$("a").on("click", function()
{
var form = $("<form></form>");
form.attr(
{
id : "formform",
// The location given in the link itself
action : $(this).attr("href"),
method : "GET",
// Open in new window/tab
target : "_blank"
});
$("body").append(form);
$("#formform").submit();
$("#formform").remove();
// Prevent the link from opening normally
return false;
});
});
For each link element, it:
Creates a form
Gives it attributes
Appends it to the DOM so it can be submitted
Submits it
Removes the form from the DOM, removing all traces *Insert evil laugh*
Now you have a new tab/window loading "https://google.nl" (or any URL you want, just replace it). Unfortunately when you try to open more than one window this way, you get an Popup blocked messagebar when trying to open the second one (the first one is still opened).
More information on how I got to this method is found here:
Opening new window/tab without using window.open or window.location.href
Click handlers on anchor tags are a special case in jQuery.
I think you might be getting confused between the anchor's onclick event (known by the browser) and the click event of the jQuery object which wraps the DOM's notion of the anchor tag.
You can download the jQuery 1.3.2 source here.
The relevant sections of the source are lines 2643-2645 (I have split this out to multiple lines to make it easier to comprehend):
// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
if (
(!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) &&
elem["on"+type] &&
elem["on"+type].apply( elem, data ) === false
)
event.result = false;
You can use jQuery to select the jQuery object for that element. Then, get the underlying DOM element and call its click() method.
By id:
$("#my-link").each(function (index) { $(this).get(0).click() });
Or use jQuery to click a bunch of links by CSS class:
$(".my-link-class").each(function (index) { $(this).get(0).click() });
Trigger a hyperlink <a> element that is inside the element you want to hookup the jQuery .click() to:
<div class="TopicControl">
<div class="articleImage">
<img src="" alt="">
</div>
</div>
In your script you hookup to the main container you want the click event on. Then you use standard jQuery methodology to find the element (type, class, and id) and fire the click. jQuery enters a recursive function to fire the click and you break the recursive function by taking the event 'e' and stopPropagation() function and return false, because you don't want jQuery to do anything else but fire the link.
$('.TopicControl').click(function (event) {
$(this).find('a').click();
event.stopPropagation();
return false;
});
Alternative solution is to wrap the containers in the <a> element and place 's as containers inside instead of <div>'s. Set the spans to display block to conform with W3C standards.
It does nothing because no events have been bound to the event. If I recall correctly, jQuery maintains its own list of event handlers that are bound to NodeLists for performance and other purposes.
If you need this feature for one case or very few cases (your whole application is not requiring this feature). I would rather leave jQuery as is (for many reasons, including being able to update to newer versions, CDN, etc.) and have the following workaround:
// For modern browsers
$(ele).trigger("click");
// Relying on Paul Irish's conditional class names,
// <https://www.paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/>
// (via HTML5 Boilerplate, <https://html5boilerplate.com/>) where
// each Internet Explorer version gets a class of its version
$("html.ie7").length && (function(){
var eleOnClickattr = $(ele).attr("onclick")
eval(eleOnClickattr);
})()
To open hyperlink in the same tab, use:
$(document).on('click', "a.classname", function() {
var form = $("<form></form>");
form.attr(
{
id : "formid",
action : $(this).attr("href"),
method : "GET",
});
$("body").append(form);
$("#formid").submit();
$("#formid").remove();
return false;
});

Categories