jQuery - what does the newHeader property/field do? - javascript

I admit to being somewhat of a copy and paste JavaScript developer (with a strong background in other languages). I'm using the jQuery accordion, and using cookies to save the selected accordion section. I found some code which I integrated into my code. The key section is as follows.
change: function (event, ui) {
var index = $(this).find("h3").index(ui.newHeader[0]);
$.cookie(accordion, index);
}
This works, but I hate using code I don't understand. I understand that the index is discovered by using the find method (which makes the assumption that I don't have any h3s within the content), but what I don't understand is what the ui.newHeader[0] is doing. What is the newHeader array, and what is its purpose here?
Thanks,
Erick

Looking at the source for jquery.ui.accordion.js, it's just an object that contains the newly selected element.
You can see for yourself if you just check out the source:
// find elements to show and hide
var toShow = clicked.next(),
toHide = this.active.next(),
data = {
options: options,
newHeader: clickedIsActive && options.collapsible ? $([]) : clicked,
oldHeader: this.active,
newContent: clickedIsActive && options.collapsible ? $([]) : toShow,
oldContent: toHide
},
down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
this.active = clickedIsActive ? $([]) : clicked;
this._toggle( toShow, toHide, data, clickedIsActive, down );
return;
},
newHeader is not an array, it's an object that represents the newly selected element. The code you posted finds all h3 elements in the accordion element, and then it takes the index of the newHeader. The element that newHeader represents changes each time the accordion changes.

It's exposed by the accordion widget. The newheader property holds the header of the activated element that the accordion opened.
See also the doc for the change event.

Related

Google Tag Manager: return position in the DOM of the tracked link/element, {{Click Element}} variable

Background description:
I am tracking clicks on links on a web page using Google Tag Manager (GTM) and have set this up successfully, together with python code to extract the data from the API. However, GTM currently doesn't return any indication of the position on the page of the link clicked. I would like to put in the Event Label the position in the DOM of the clicked element.
The purpose of doing this is twofold: Firstly, so I can distinguish identical links (i.e. links which have the same link text and which go to same destination). Secondly, so that I can identify which of the many sections of the page the link is in: is it in the sidebar, on the top bar, in the main content etc.
Problem
I have tried using the {{Click Element}} variable, but this only returns the href of the link, not the position on the DOM.
This is puzzling because when I go into Preview mode in GTM and have a look at the data layer, I see very clearly that there was a push API call to send the DOM location:
dataLayer.push({
event: 'gtm.click',
gtm.element: 'https:// [rest of URL] : html.js.yes-js.js_active >
[lots more DOM elements separated with >s of which I am only including a few]
> div#page-container > div#et-main-area > div#main-content > h3.rpwe-title > a',
gtm.elementClasses: '',
gtm.elementId: '',
gtm.elementTarget: '',
gtm.elementUrl: __*~~URL~~*__,
gtm.uniqueEventId: 425
})
And in the "Variables" tab of the GTM preview page, Click Element shows up just as above: it is a Data Layer Variable, it is a string, and its value is given as first the URL, then a colon, followed by the long description of where the link is on the DOM, with all the parents starting from the root of the document, all the way down until the clicked link.
So why, when I add the {{Click Element}} variable to the GTM Event Label, does this only return the href of the link, not the position on the DOM? I submit the changes and publish a new version in GTM, then go to Google Analytics, and only the link href shows there.
What I have tried
Going into the DevTools in chrome (right click, Inspect) and entering the console and typing dataLayer shows that some events, e.g. gtm.elementVisibility, do have a gtm.element object which seems pretty vast. In this gtm.element object, you can click on parentElement which then has its own parentElement and so on until the top level of the DOM. So this data is clearly all there.
To access it I tried a Custom JS variable in GTM which used JSON.stringify to convert Click Element into a string. This didn't work, I suspect because the gtm.element object is too complex, with nested objects and circular references to many locations within the object.
I don't know Javascript, I am groping in the dark with it, I just want to get a nice string through to Google Analytics and from there to my python code where I can manipulate it as necessary.
Possible solution which isn't practical
In GTM I could make multiple triggers for the same event, and specify for each trigger that Click Element matches the CSS Selector for each of the sections of the page. However that would require a separate trigger for each section, which would get messy if I ever need to update the trigger settings. It also wouldn't help me distinguish between identical links.
Question
Why isn't the above working nicely, and how can I accomplish this (preferably with minimal javascript coding...)? Thanks!
Simo Ahava has a great blog post about exactly this question, at https://www.simoahava.com/analytics/create-css-path-variable-for-click-element/ (no idea why multiple google searches didn't find this...)
As suspected in the question, he explains that Click Element is not actually a string with the location of the link in the DOM, as the GTM Preview mode indicated. It is a complex HTML element.
He goes on to provide javascript code which takes this complex Click Element and extracts the location in the DOM of the link:
function() {
// copied from https://www.simoahava.com/analytics/create-css-path-variable-for-click-element/
// Build a CSS path for the clicked element
var originalEl = {{Click Element}};
var el = originalEl;
if (el instanceof Node) {
// Build the list of elements along the path
var elList = [];
do {
if (el instanceof Element) {
var classString = el.classList ? [].slice.call(el.classList).join('.') : '';
var elementName = (el.tagName ? el.tagName.toLowerCase() : '') +
(classString ? '.' + classString : '') +
(el.id ? '#' + el.id : '');
if (elementName) elList.unshift(elementName);
}
el = el.parentNode
} while (el != null);
// Get the stringified element object name
var objString = originalEl.toString().match(/\[object (\w+)\]/);
var elementType = objString ? objString[1] : originalEl.toString();
var cssString = elList.join(' > ');
// Return the CSS path as a string, prefixed with the element object name
return cssString ? elementType + ': ' + cssString : elementType;
}
}
This returns something looking like 'HTMLDivElement: html > body > div#blog > div.hasCoverMetaIn#main' where div is the type of element, element class(es) follows the period (.), and element id follows the hash (#).
So thank you to Simo Ahava, hope it is OK to share his code here. See his article for fuller explanation

TinyMCE update Toolbar (after init) when you have Editor on method

I'm working on a Google Fonts plugin for WordPress and I try to have the same effect as the core WYSIWYG editor. Basically when you click on element (inside the Editor) with font class I want to get the class and then based on that reload the font family/style listbox in the Toolbar.
(I found couple of hacks here on SO like this one Proper Way Of Modifying Toolbar After Init in TinyMCE but nothing that works like the WP core example)
There is the same functionality when you click on P, H1, H3, H3 ... How they do it? Can you point me at least to the JS file in WordPress distro; I think I can figure it out if see the code.
Here is GIF that demonstrates what I'm talking about. Thanks in advance.
I found the solution and because it's not a hack, like the other ones I found on SO, I will post it in here and hopes it will help anyone else that's trying to do something similar.
First to access the button/listbox need to use onpostrender with a callback function.
editor.addButton( 'developry_google_font_family_button', {
type : 'listbox',
onpostrender : fontFamilyNodeChange,
value : '',
...
Next the callback function should look something like this:
function fontFamilyNodeChange() {
var listbox = this;
editor.on('NodeChange', function( e ) {
// This next part is specific for my needs but I will post it as an example.
var selected = [];
if ( $( e.element ).hasClass( 'mce-ga' ) ) { // this a class I add to all elements that have google fonts format
// Then I strip the classes from classList that I don't need and add the rest into an array (e.g ['roboto', '100'])
var gfont_options = $( e.element ).attr('class')
.replace('mce-ga', '')
.replace('mce-family-', '')
.replace('mce-weight-', '')
.trim()
.split(' ');
selected.push( gfont_options );
// At end I add the new value to listbox select[0][0] (e.g. 'roboto')
listbox.value(selected[0][0]);
}
});
}
And here is an example:

Add a link to a form with jQuery (userscript) in Firefox

I'm trying to add a search link to an online form with a userscript using jQuery. I don't work too much in firefox and I feel like things that would normally work in chrome don't in ff 9/10 times for me. But anyway... this needs to be with ff.
I'm taking the text from a <p> element and creating a search url out of it (or trying to). Right now this is the function I'm trying that should be doing it... but it's doing nothing, not even any errors in console
$(function() {
var companyName = $('p')[7]; // Element that contains the name text
var companyText = companyName.text(); // Retrieve the text from element
var mixRankUrl = $("<a></a>").innerHTML("Search Mixrank"); // Create an <a> element
mixRankUrl.href = 'https://mixrank.com/appstore/sdks?search=' + companyText; // Define the href of the a element
var sdkPara = $('label.control-label')[10]; // Where I want it to go
sdkPara.append(mixRankUrl); // Append the element
});
Also, whoever wrote the html uses hardly any ids, and most classes are assigned to 10 or more elements... so unless there's a better way, I'm sort of stuck using node selectors (which stay the same form to form).
The problem is that you try to use jQuery method on DOM element. Don't understand why you don't have any errors with your code.
For exemple : $('p')[7] return a DOM element while $('p').eq(7) return a JQuery object. So you can't use a jQuery method like text() on your DOM element. You need to deal with jQuery object.
For the same reason, you had a problem with the declaration of your label object and with the modification of the href attribute of your link.
Try like this :
$(function() {
var companyName = $('p').eq(7); // Element that contains the name text
var companyText = companyName.text(); // Retrieve the text from element
var sdkPara = $('label.control-label').eq(10); // Where I want it to go
var mixRankUrl = $('<a>',{
text: 'Search Mixrank',
href: 'https://mixrank.com/appstore/sdks?search=' + companyText
}).appendTo(sdkPara); // Append the element
});

scrollIntoView not defined

I have the following problem.
I have a tree view control in .NET and I have created a autocomplete search box to search the tree for a name - which then I can select and it highlights the item with a selected class.
The tree view is pretty long so I have given it a height and overflow scroll.
The problem is that I want view or scroll down to the selected item when I've searched for it.
So i have created the following script to scrollIntoView it but this doesn't seem to work:
function search_itemSelected(sender, e) {
var hdSearchID = $get('<%= hdSearchID.ClientID %>');
hdSearchID.value = e.get_value();
var selectedElement = $("div.node.cen.selected"); // This works
if (selectedElement != null) {
selectedElement[0].scrollIntoView = 10; // This keeps coming back as undefined
}
}
First: change
if (selectedElement != null)
to
if (selectedElement.length)
because $("div.node.cen.selected"); will never return null. jQuery always returns a jQuery object (empty or not, but a jQuery object)
So in the case where it is empty, the selectedElement[0] will return undefined and thus the scrollIntoView does not exist..
Second: scrollIntoView is a function and so you do not assign a value to it. You need to call it with
selectedElement[0].scrollIntoView();
scrollIntoView is used with element you want to show, like that :
el.scrollIntoView(true);
Note:
Scrolls the window until the element is visible.
Syntax:
document.all.elementID.scrollIntoView(param1)
Parameters:
param1 Optional; true or false, indicating whether the top of the element
is scrolled to the top of the window or the bottom of the
element is scrolled to the bottom of the window.
Moreover, scrollIntoView() can only be used with some tags
http://www.java2s.com/Code/JavaScriptReference/Javascript-Methods/scrollIntoViewisappliedto.htm

Is there a cross-browser way to ignore opacity when using jquery's clone()?

I'm using tables in my document, and I want to be able to have a user submit a new item to a list, then have it "automagically" appear at the top of the list (yes, this would be easier with DIVs but working with what I have).
I'm using jQuery, and clone() to create a copy of the most recent table row, then using fadeIn() to display the new item after I update and add it to the top of the list. Because internally jQuery converts elements (assuming DIVs) to 'block', I'm also changing the css class to 'table-row'. It works fine.
The whole code is here:
var row = $("tbody tr:first").clone().hide(); // clone and then set display:none
row.children("td[class=td-date]").html("today");
// set some properties
row.children("td[class=td-data]").html("data");
row.children("td[class=td-type]").html("type");
// fadeIn new row at the top of the table.
row.insertBefore("tbody tr:first").stop().fadeIn(2000).css("display","table-row");
The problem is that if I run the process too quickly - i.e. before the fadeIn completes, the "clone()" command ends up cloning the opacity as well.
I can actually get it to work in Firefox using by adjusting the first line above:
var row = $("tbody tr:first").clone().css("opacity","1").hide();
My concern now is that I'm not sure that any of this is being done efficiently, and/or that "opacity" is cross-browser safe to rely upon.
Has anyone done something like this before, and can offer any pointers on a more reliable approach?
opacity as a jQuery css attribute is safe cross-browser as it irons out the browser differences in the implementation. Here's the source
// IE uses filters for opacity
if ( !jQuery.support.opacity && name == "opacity" ) {
if ( set ) {
// IE has trouble with opacity if it does not have layout
// Force it by setting the zoom level
elem.zoom = 1;
// Set the alpha filter to set the opacity
elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
(parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
}
return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '': "";
}
The following works.
Working Demo - add /edit to the URL to play with it.
// stop previous animation on the previous inserted element
var prevRow = $("tbody tr:first").stop(true,true);
var row = prevRow.clone();
row.children("td.td-date").text("today");
row.children("td.td-data").text("data");
row.children("td.td-type").text("type");
row.fadeIn(2000).prependTo("tbody");
There is no reason to use hide on your clone. The clone isn't added to the dom yet so it can't be visible.
Try this:
var row = $("tbody tr:first").clone(); // clone
// set some properties
row.children("td[class=td-date]").html("today");
row.children("td[class=td-data]").html("data");
row.children("td[class=td-type]").html("type");
// fadeIn new row at the top of the table.
row.insertBefore("tbody tr:first").fadeOut(0).fadeIn(2000).css("display","table-row");
I think jQuery will handle it if you do this.
var row = $("tbody tr:first").clone().fadeIn(0).hide();

Categories