| 1 |
/** |
| 2 |
* Collapse & Expand Text Feature |
| 3 |
* King Addons for Elementor |
| 4 |
*/ |
| 5 |
|
| 6 |
(function($) { |
| 7 |
'use strict'; |
| 8 |
|
| 9 |
class KingAddonsCollapseExpandText { |
| 10 |
constructor() { |
| 11 |
this.init(); |
| 12 |
} |
| 13 |
|
| 14 |
init() { |
| 15 |
// Initialize on document ready |
| 16 |
$(document).ready(() => { |
| 17 |
this.initElements(); |
| 18 |
}); |
| 19 |
|
| 20 |
// Re-initialize when Elementor frontend loads (for preview mode) |
| 21 |
if (typeof elementor !== 'undefined') { |
| 22 |
elementor.hooks.addAction('frontend/element_ready/global', () => { |
| 23 |
this.initElements(); |
| 24 |
}); |
| 25 |
} |
| 26 |
|
| 27 |
// Re-initialize when widgets are loaded in preview |
| 28 |
$(window).on('elementor/frontend/init', () => { |
| 29 |
elementorFrontend.hooks.addAction('frontend/element_ready/global', () => { |
| 30 |
this.initElements(); |
| 31 |
}); |
| 32 |
}); |
| 33 |
} |
| 34 |
|
| 35 |
initElements() { |
| 36 |
// Find all elements with collapse/expand enabled |
| 37 |
$('.kng-collapse-expand-yes').each((index, element) => { |
| 38 |
this.setupCollapseExpand($(element)); |
| 39 |
}); |
| 40 |
} |
| 41 |
|
| 42 |
setupCollapseExpand($element) { |
| 43 |
// Skip if already initialized |
| 44 |
if ($element.data('kng-collapse-initialized')) { |
| 45 |
return; |
| 46 |
} |
| 47 |
|
| 48 |
// Check if element has collapse/expand enabled |
| 49 |
if (!$element.hasClass('kng-collapse-expand-yes')) { |
| 50 |
return; |
| 51 |
} |
| 52 |
|
| 53 |
// Find the main text content |
| 54 |
let $textContent = this.findTextContent($element); |
| 55 |
|
| 56 |
if (!$textContent || $textContent.length === 0) { |
| 57 |
return; |
| 58 |
} |
| 59 |
|
| 60 |
// Get settings from data attributes |
| 61 |
const collapsedHeight = parseInt($element.data('kng-collapse-height')) || 80; |
| 62 |
const showMoreText = $element.data('kng-collapse-show-more') || 'Read more'; |
| 63 |
const showLessText = $element.data('kng-collapse-show-less') || 'Read less'; |
| 64 |
const animationDuration = parseInt($element.data('kng-collapse-duration')) || 300; |
| 65 |
const buttonPosition = $element.data('kng-collapse-position') || 'right'; |
| 66 |
|
| 67 |
// Check if text needs collapsing |
| 68 |
if (!this.needsCollapsing($textContent, collapsedHeight)) { |
| 69 |
return; |
| 70 |
} |
| 71 |
|
| 72 |
// Mark as initialized |
| 73 |
$element.data('kng-collapse-initialized', true); |
| 74 |
|
| 75 |
// Wrap content and add button |
| 76 |
this.wrapContent($textContent, $element, showMoreText, showLessText, buttonPosition); |
| 77 |
|
| 78 |
// Set initial state |
| 79 |
this.setCollapsedState($element); |
| 80 |
|
| 81 |
// Bind button click |
| 82 |
this.bindButtonClick($element, animationDuration); |
| 83 |
} |
| 84 |
|
| 85 |
findTextContent($element) { |
| 86 |
// For styled text builder |
| 87 |
if ($element.find('.king-addons-styled-text-builder-items').length) { |
| 88 |
return $element.find('.king-addons-styled-text-builder-items'); |
| 89 |
} |
| 90 |
|
| 91 |
// For default text widget |
| 92 |
if ($element.find('.elementor-text-editor').length) { |
| 93 |
return $element.find('.elementor-text-editor'); |
| 94 |
} |
| 95 |
|
| 96 |
// For heading widget |
| 97 |
if ($element.find('.elementor-heading-title').length) { |
| 98 |
return $element.find('.elementor-heading-title'); |
| 99 |
} |
| 100 |
|
| 101 |
// For other text content |
| 102 |
if ($element.find('p, h1, h2, h3, h4, h5, h6, div').length) { |
| 103 |
return $element.find('p, h1, h2, h3, h4, h5, h6, div').first(); |
| 104 |
} |
| 105 |
|
| 106 |
return null; |
| 107 |
} |
| 108 |
|
| 109 |
needsCollapsing($content, maxHeight) { |
| 110 |
// Create a temporary clone to measure |
| 111 |
const $clone = $content.clone(); |
| 112 |
$clone.css({ |
| 113 |
'position': 'absolute', |
| 114 |
'top': '-9999px', |
| 115 |
'left': '-9999px', |
| 116 |
'width': $content.width() || 'auto', |
| 117 |
'visibility': 'hidden' |
| 118 |
}); |
| 119 |
|
| 120 |
$('body').append($clone); |
| 121 |
|
| 122 |
const contentHeight = $clone.height(); |
| 123 |
|
| 124 |
$clone.remove(); |
| 125 |
|
| 126 |
return contentHeight > maxHeight; |
| 127 |
} |
| 128 |
|
| 129 |
wrapContent($content, $element, showMoreText, showLessText, buttonPosition) { |
| 130 |
// Wrap the content |
| 131 |
$content.wrap('<div class="kng-collapse-expand-content collapsed"></div>'); |
| 132 |
|
| 133 |
// Add button wrapper and button |
| 134 |
const buttonHtml = ` |
| 135 |
<div class="kng-collapse-expand-button-wrapper"> |
| 136 |
<button class="kng-collapse-expand-button" |
| 137 |
data-show-more-text="${showMoreText}" |
| 138 |
data-show-less-text="${showLessText}" |
| 139 |
aria-expanded="false"> |
| 140 |
${showMoreText} |
| 141 |
</button> |
| 142 |
</div> |
| 143 |
`; |
| 144 |
|
| 145 |
$element.find('.kng-collapse-expand-content').after(buttonHtml); |
| 146 |
} |
| 147 |
|
| 148 |
setCollapsedState($element) { |
| 149 |
const $content = $element.find('.kng-collapse-expand-content'); |
| 150 |
const $button = $element.find('.kng-collapse-expand-button'); |
| 151 |
|
| 152 |
$content.addClass('collapsed').removeClass('expanded'); |
| 153 |
$button.attr('aria-expanded', 'false'); |
| 154 |
$button.text($button.data('show-more-text')); |
| 155 |
} |
| 156 |
|
| 157 |
setExpandedState($element) { |
| 158 |
const $content = $element.find('.kng-collapse-expand-content'); |
| 159 |
const $button = $element.find('.kng-collapse-expand-button'); |
| 160 |
|
| 161 |
$content.addClass('expanded').removeClass('collapsed'); |
| 162 |
$button.attr('aria-expanded', 'true'); |
| 163 |
$button.text($button.data('show-less-text')); |
| 164 |
} |
| 165 |
|
| 166 |
bindButtonClick($element, animationDuration) { |
| 167 |
const $button = $element.find('.kng-collapse-expand-button'); |
| 168 |
const $content = $element.find('.kng-collapse-expand-content'); |
| 169 |
|
| 170 |
$button.on('click', (e) => { |
| 171 |
e.preventDefault(); |
| 172 |
|
| 173 |
if ($content.hasClass('collapsed')) { |
| 174 |
// Expand |
| 175 |
this.setExpandedState($element); |
| 176 |
} else { |
| 177 |
// Collapse |
| 178 |
this.setCollapsedState($element); |
| 179 |
|
| 180 |
// Scroll to element top if needed |
| 181 |
this.scrollToElementIfNeeded($element); |
| 182 |
} |
| 183 |
}); |
| 184 |
} |
| 185 |
|
| 186 |
scrollToElementIfNeeded($element) { |
| 187 |
const elementTop = $element.offset().top; |
| 188 |
const viewportTop = $(window).scrollTop(); |
| 189 |
const elementHeight = $element.height(); |
| 190 |
|
| 191 |
// If element top is above viewport, scroll to it |
| 192 |
if (elementTop < viewportTop) { |
| 193 |
$('html, body').animate({ |
| 194 |
scrollTop: elementTop - 20 |
| 195 |
}, 300); |
| 196 |
} |
| 197 |
} |
| 198 |
|
| 199 |
|
| 200 |
} |
| 201 |
|
| 202 |
// Initialize the collapse/expand functionality |
| 203 |
new KingAddonsCollapseExpandText(); |
| 204 |
|
| 205 |
})(jQuery); |
| 206 |
|